Frida is a dynamic code instrumentation toolkit. It lets you inject your script into black-box processes(No source code needed). It allows you to inject your own code and to programmatically and interactively inspect and change running processes.
Frida, on the other hand, allows you to interact with the Android APK so you can inject code to bypass many of the techniques developers use to secure the apps. Some examples include bypassing the login screen to authenticate without a password or disabling SSL pinning to allow the hacker to see all the network traffic between your app and any backend servers.
Many of the method calls you make in your Android app can be hijacked or overridden by Frida for purposes that were never intended. By injecting JavaScript code, Frida can disable or enable a switch or pass fake parameters to gain access to information that would not otherwise be available.
Before starting with Frida, it is assumed that the reader has prior knowledge of Javascript. It would help to better Understanding of API calls and build your own custom tools
First User have to create an file in which patching method written like Accessing any method value, or changing return value . The patched method is sent from the computer of the User to the agent frida (installed on android device), The agent being inserted in the application on the mobile. And there, the patch (in js) is compiled and used to patch application
While the application is running (the user of the phone starts the app)
The ART loads the app’s .oat file to run it and the .so containing FridaDroid + patch is started. In the .so, FridaDroid hook ART functions Get the references of the target method using the hooks obtained in (1). Compile the patch and modify the reference to the target method with the binaries obtained at the end of the compilation. The application continues to run :
Given a process, frida allows us to intercept the memory of the process. We can dump classes/modules/functions. We can intercept functions/method invocation to read arguments / return values. We can modify these arguments / return values --> allow us to do cool bypasses without having to recompile the target binary/application (oh no... all the packaging and jar signing nightmares...).
- Install genymotion.
- Install the app in genymotion
- Check if device is accessible
$ pip install frida-tools
-> % adb devices
List of devices attached
192.168.56.101:5555 device
This means that the device is successfully connected via USB.
- Shell access
-> % adb shell
vbox86p:/ #
Ok, we have shell access. Genymotion comes with root by default. Yay, no jail breaking needed!
- Set up frida server inside android. It has to be inside android because we are going to intercept target process.
-> % frida-ps -U
Failed to enumerate processes: unable to connect to remote frida-server: closed
The above is what you get when frida server is not running.
Firstly, check your host's frida version.
-> % frida --version
12.4.8
Secondly, check the architecture of the android with the adb shell like the following.
vbox86p:/tmp # uname -a
Linux localhost 4.4.34-genymotion #4 SMP PREEMPT Mon Oct 1 15:25:53 CEST 2018 i686
Taking note of the above frida version and the target architecture, we have to find the correct binary from here. I chosed frida-server-12.4.8-android-x86.xz
, you have to choose the correct one depending on the above checks.
- Extract with 7z and upload the file to android.
-> % adb push frida-server-12.4.8-android-x86 /tmp
frida-server-12.4.8-android-x86: 1 file pushed. 79.4 MB/s (32873600 bytes in 0.395s)
- Run the frida server inside adb shell
vbox86p:/tmp # chmod +x frida-server-12.4.8-android-x86
vbox86p:/tmp # ./frida-server-12.4.8-android-x86 -l 0.0.0.0 -D
vbox86p:/tmp # ps -A | grep frida
root 4186 1 97064 56356 poll_schedule_timeout e85e1ba9 S frida-server-12.4.8-android-x86
root 4190 4186 13272 4720 poll_schedule_timeout ec079ba9 S frida-helper-32
- Test if frida server is set up properly.
-> % frida-ls-devices
Id Type Name
------------------- ------ --------------------------
local local Local System
192.168.56.101:5555 usb Genymotion APP-PT8.0
tcp remote Local TCP
-> % frida-ps -U
PID Name
---- -----------------------------------------------
154 adbd
373 android.hardware.camera.provider@2.4-service
374 android.hardware.configstore@1.0-service
399 android.hardware.gnss@1.0-service
375 android.hardware.graphics.allocator@2.0-service
139 android.hardware.keymaster@3.0-service
376 android.hardware.sensors@1.0-service
....
....
Cool. We're done with set up.
-> % frida-ps -Uai
PID Name Identifier
---- --------------------------------------------- ----------------------------------------------
614 Android Keyboard (AOSP) com.android.inputmethod.latin
504 Android System android
1427 Blocked Numbers Storage com.android.providers.blockednumber
504 Call Management com.android.server.telecom
2643 ConfigUpdater com.google.android.configupdater
1427 Contacts Storage com.android.providers.contacts
504 Fused Location com.android.location.fused
2591 Google Partner Setup com.goog.............
I like to use this to find out the name of the app and its identifier.
We can use the app's identifier as the filename to frida.
-> % frida -U -f com.android.calculator2 --no-pause
____
/ _ | Frida 12.4.8 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at http://www.frida.re/docs/home/
Spawned `com.android.calculator2`. Resuming main thread!
[Genymotion CENTURION-PT8.0::com.android.calculator2]->
We should see a calculator pop up inside genymotion. The above is a frida console shell that accept frida's javascript. This console is useful because of the autocomplete feature. We can use this console to help us build our frida javascript scripts.
From Sniffing https traffic on Android 11, installing a certificate from Burp Suite on an Android 11 emulator:
openssl x509 -inform der -in burp.cer -out certificate.pem
cp certificate.pem `openssl x509 -inform pem -subject_hash_old -in certificate.pem | head -1`.0
./emulator -avd rRoot -writable-system
adb push 9a5ba575.0 //sdcard/Download
adb root
adb shell avbctl disable-verification
adb reboot
adb root
adb remount
adb shell
cp /sdcard/Download/9a5ba575.0 /system/etc/security/cacerts/
chmod 644 /system/etc/security/cacerts/9a5ba575.0
reboot
# Start PolarProxy
PolarProxy -p 443,80 -w polarproxy.pcap
# Setup adb reverse connection
adb reverse tcp:443 tcp:443
# iptables setup
adb root
adb shell
iptables -t nat -A OUTPUT -p tcp --dport 443 -j DNAT --to-destination 127.0.0.1:443
Due to privacy, i will redact the actual app name with APP
The following is written by refering the Javascript API from here.
Template of callback
var callback = {
'onMatch': function(arg1){
console.log(arg1);
},
'onComplete': function() {
console.log("done");
},
'onError': function(){
console.log("There is error");
}
};
The above is how you can define a callback javascript object. The number of arguments passed does not matter. If the API invoke does not use up till the second / third /... parameter, those parameters will return as undefine.
Running most of frida's Java API code will require us to obtain the thread that has access to the VM. There is a wrapper Java API that helps us achieve that.
Java.perform(function(){
//put what ever you would like to execute inside here.
});
With these, we can start enumerating the classes from our android app.
enumclasses.js
var callback = {
'onMatch': function(cname){
//lets just print out the class name.
console.log(cname);
},
'onComplete': function() {
console.log("done");
},
'onError': function(){
console.log("There is error");
}
};
Java.perform(function(){
Java.enumerateLoadedClasses(callback); //onMatch: function (className)
});
The above will print out class names from the application.
-> % frida -U -f APP --no-pause -l enumclasses.js
____
/ _ | Frida 12.4.8 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at http://www.frida.re/docs/home/
Spawned `APP`. Resuming main thread!
[Genymotion APP-PT8.0::APP]-> org.apache.http.ProtocolVersion
org.apache.http.HttpResponse
org.apache.http.message.AbstractHttpMessage
org.apache.http.HttpHost
org.apache.http.conn.params.ConnPerRoute
org.apache.http.impl.conn.tsccm.RefQueueWorker
org.apache.http.conn.params.ConnManagerParams
org.apache.http.params.AbstractHttpParams
org.apache.http.impl.conn.IdleConnectionHandler
org.apache.http.conn.ConnectionReleaseTrigger
org.apache.http.HttpRequestInterceptor
org.apache.commons.logging.impl.WeakHashtable
org.apache.commons.logg......
....
Awesome! We can access class names. With this, we can start building on more actions we would like to take with the class name.
Note that the classes dumped are classes accessible by class loader during runtime. Java uses lazy class loading which means that classes are only loaded when first encountered. Hence, if an activity does not get executed, classes that are not yet encountered will not be dumped.
Supposed there is a method from a package ( com.scottyab.rootbeer
) that check if a phone has been rooted using a method call ( RootBeer.isRooted()
).
Main idea:
- We can access the class directly using its full name obtained by decompiling the apk.
- Modify the implementation.
- Trigger the correct Activity and appropriate sequence of actions to reach the code we are targetting --> the invocation will run our implementation instead of the actual compiled byte code. ( See others.md for starting hidden activities )
BypassRoot.js
Java.perform(function(){
//The Java "use" helps us load the class into memory --> return a javascript wrapper of the class.
//we can access methods using dot notation and overwrite its implementation.
var RootBeer = Java.use("com.scottyab.rootbeer.RootBeer");
RootBeer.isRooted.implementation = function(){
console.log("RootBeer isRooted returns false");
return false;
}
});
Basically, we are overwriting the method isRooted() to always return false.
-> % frida -U -p 6713 -l bypassrootcheck.js --no-pause
____
/ _ | Frida 12.4.8 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at http://www.frida.re/docs/home/
[Genymotion APP-PT8.0::PID::6713]-> RootBeer isRooted returns false
RootBeer isRooted returns false
[Genymotion APP-PT8.0::PID::6713]->
The app Activity was started and frida was used to hook onto the specific running process via pid. After performing the action that trigger the root check, we see that our console printed out "RootBeer isRooted returns false".
Finding hidden Activity
Sometimes, an app has hidden activity that is not reachable from MAIN LAUNCHER. We need to take a look at AndroidManifest.xml file to find all activities defined.
It is possible to draw a transition diagram to map out the transitions of the activities where the activities not found reachable by transitions are considered hidden ones.
Accessing hidden Activity
Using Drozer shell, we can do a few cool tricks.
Like Frida server, our drozer server has to be on the android phone. Instead of command-line, drozer is a mobile app. Launching drozer will open up a random port that our drozer client can connect to. The APK can be downloaded from here.
-> % drozer console connect --server 192.168.56.101:31415
:0: UserWarning: You do not have a working installation of the service_identity module: 'No module named service_identity'. Please install it from <https://pypi.python.org/pypi/service_identity> and make sure all of its dependencies are satisfied. Without the service_identity module, Twisted can perform only rudimentary TLS client hostname verification. Many valid certificate/hostname mappings may be rejected.
Selecting bb136700a5ee361e (Genymotion APP-PT8.0 8.0.0)
.. ..:.
..o.. .r..
..a.. . ....... . ..nd
ro..idsnemesisand..pr
.otectorandroidsneme.
.,sisandprotectorandroids+.
..nemesisandprotectorandroidsn:.
.emesisandprotectorandroidsnemes..
..isandp,..,rotectorandro,..,idsnem.
.isisandp..rotectorandroid..snemisis.
,andprotectorandroidsnemisisandprotec.
.torandroidsnemesisandprotectorandroid.
.snemisisandprotectorandroidsnemesisan:
.dprotectorandroidsnemesisandprotector.
drozer Console (v2.4.4)
dz> help
drozer: Android Security Assessment Framework
Type `help COMMAND` for more information on a particular command, or `help
MODULE` for a particular module.
Commands:
cd contributors env help load permissions set unset
clean echo exit list module run shell
Miscellaneous help topics:
intents
dz> list
app.activity.forintent Find activities that can handle the given intent
app.activity.info Gets information about exported activities.
app.activity.start Start an Activity
app.broadcast.info Get information about broadcast receivers
app.broadcast.send Send broadcast using an intent
app.broadcast.sniff Register a broadcast receiver that can sniff particular intents
app.package.attacksurface Get attack surface of package
app.package.backup Lists packages that use the backup API (returns true on FLAG_ALLOW_BACKUP)
app.package.debuggable Find debuggable packages
app.package.info Get informa..........
...............
By looking at the commands listed above, we can see that drozer can be used for information gathering / sending intents without having to build an app / starting activities...
Showing permissions
dz> run app.package.info -f <app name>
Find out exported activities
dz> run app.activity.info -a <app identifier>
Start the exported activity
dz> run app.activity.start --component <app identifier> <activity full qualified name>
Finding attack surface
run app.package.attacksurface