In this blog post, we will have a look at Frida, which is one of the really interesting tools for mobile application security analysis.
This is also something we cover in-depth in our Advanced Android and iOS Exploitation training for which you can register here – Training Link
Even if you have never used Frida, this post will serve as a guide for you to get started into the world of Frida for Mobile application security analysis and exploitation.
Here’s what we are going to cover in this blog post:
- Introduction to Frida
- Setting it up on iOS
- Connecting to an iOS process using Frida
- Dumping class and method information
- Runtime manipulation of iOS apps using Frida
- Conclusion
Introduction to Frida
Frida is a dynamic code instrumentation toolkit allowing you to hook into applications, inject your own JavaScript while getting complete access to the memory and functions.
The tool is written by Ole André V. Ravnås (@oleavr) and also has a pretty active IRC channel where you can jump in to discuss ideas, questions and new features with other like minded people who have worked on Frida. You can join the IRC at #frida on irc.freenode.net .
Some of the use cases of Frida (depending on for which purpose you are using it) are –
- Hooking into a specific function and changing the return value
- Analysing custom protocols in place and sniffing/decrypting the traffic on the fly
- Performing debugging of your own applications
- Dumping class and method information from an iOS app
- And so on.
The reason why I mentioned “and so on” is because Frida could be used for a number of different purposes. The API of Frida is what makes it so powerful and a first choice for building your own security or analysis tools. There have been several tools which are built on Frida including Needle and AppMon.
One of the other things which make Frida so useful is its ability to work on non-jailbroken devices. In order to run Frida to debug applications on non-jailbroken devices, you can use tools such as Swizzler2 which modifies the application to add the FridaGadget dylib in the app.
Setting up Frida for iOS
Setting up Frida to perform iOS application security is fairly straightforward. You need to perform setup both on the iOS device as well as your host machine.
To install Frida server on your iOS device, follow the below steps.
- Open Cydia app on your iOS device.
- Add a Source with the URL being: https://build.frida.re
- Open up the Source or search for Frida, and click on Modify, then Install.
To install Frida’s Python bindings on your system, launch up your terminal and type in pip install frida to install Frida’s bindings.
Connecting to an iOS process using Frida
Now that we have Frida all set up, we are ready to start using Frida and start our iOS application security assessment and exploitation journey!
The target application in our case is the Damn Vulnerable iOS Application (DVIA) by Prateek Gianchandani, available from here. The credits for most of the scripts below go to Interference Security Github’s repo available here.
We are going to be analyzing the Jailbreak Detection exercise from DVIA, which currently shows that the device is Jailbroken.
Device is Jailbroken
Let’s start by seeing a list of all the running processes on our target device:
frida-ps –U
As you can see from the screenshot above, we now have a list of all the running processes on our iOS device.
To attach to any process, we could do a frida –U process-name
and we would now be in the frida console where we can access all the different properties, memory content and functions of our target process.
Attaching to an iOS process using Frida
We can either work here in the Frida’s shell and interact with our process, or we can write our own JavaScript to obtain analyze the data that we want.
The goal for this exercise is to identify which ViewController
and function
are responsible for validating whether our device is jailbroken or not – in the Jailbreak Detection exercise of DVIA.
Let’s go ahead and write a basic Frida script to dump all the various classes and methods present in our target application. Here, we will be looking for anything related to Jailbreak, so that we are able to perform Jailbreak Bypass with the help of Frida.
Here’s how our process would look like
iOS application security process
Finding Classes for Jailbreak Detection with Frida in DVIA
Let’s get started by identifying all the classes in the application.
for (var className in ObjC.classes){
if (ObjC.classes.hasOwnProperty(className))
{console.log(className);} }
Once you run this, you’ll see that Frida is attaching to the target process (as shown below) and once it does, it will show you a ton of classes present in our target process.
Executing JavaScript with Frida on iOS
It’s often better to grep for the expected class, which in our case would contain the word Jailbreak.
As we run the above command with a grep
for Jailbreak
, we see that there is a class called JailbreakDetectionVC, as shown below.
Identifying target class of the iOS application with Frida
After it has found all the instances, you might see an error statement which is safe to ignore.
So now that our first task of finding our target class is done, let’s go ahead and figure out any interesting methods from this class.
Finding Methods for Jailbreak Detection with Frida in DVIA
To find the methods, we would need to use ObjC.classes.class-name.$methods . We will also only look for methods of our target class, which in this case is JailbreakDetectionVC.
console.log("[*] Started: Find All Methods of a Specific Class");
if (ObjC.available) {
try {
var className = "JailbreakDetectionVC";
var methods = eval('ObjC.classes.' + className + '.$methods');
for (var i = 0; i < methods.length; i++) {
try { console.log("[-] "+methods[i]); }
catch(err) { console.log("[!] Exception1: " + err.message); }
} }
catch(err) { console.log("[!] Exception2: " + err.message); } }
else { console.log("Objective-C Runtime is not available!"); }
console.log("[*] Completed: Find All Methods of a Specific Class");
Let’s go ahead and run this, and also apply a grep for strings such as Jailbreak
, Jailbroken
and Detection
as shown below.
Identifying target methods of an iOS application class with Frida
We find that there are three methods which have one of our strings namely isJailbroken
, jailbreakTest1Tapped:
and jailbreakTest2Tapped:
.
In our case, isJailbroken looks like the most likely function for detecting whether the device is Jailbroken or not and sending a return value.
Modifying return values of Methods for Jailbreak Detection with Frida in DVIA
So let’s go ahead and see what kind of return value does isJailbroken sends.
if (ObjC.available) {
try { var className = "JailbreakDetectionVC";
var funcName = "- isJailbroken";
var hook = eval('ObjC.classes.' + className + '["' + funcName + '"]');
Interceptor.attach(hook.implementation, {
onLeave: function(retval) { console.log("[*] Class Name: " + className);
console.log("[*] Method Name: " + funcName);
console.log("\t[-] Type of return value: " + typeof retval);
console.log("\t[-] Return Value: " + retval); } }); }
catch(err) { console.log("[!] Exception2: " + err.message); } }
else { console.log("Objective-C Runtime is not available!"); }
Once you run this script, press the Jailbreak Test 1 in the iOS application and you’ll see the return value being shown in the Frida console.
Finding return value of target method
Since our device is jailbroken, it shows a return value of 0x1
, which simply means that the function is returning True
.
Our next task is to overwrite this return value and patch the method, so that whenever the Jailbreak Test 1 button is pressed in the application, it returns false
, or 0x0
.
Let’s just add another line to change the return value of this specific function.
This could be done by the following line of code which changes the return value as well as logs it to the console:
newretval = ptr("0x0");
retval.replace(newretval);
console.log("\t[-] New Return Value: " + newretval);
This final script is given below.
if (ObjC.available) {
try {
var className = "JailbreakDetectionVC";
var funcName = "- isJailbroken";
var hook = eval('ObjC.classes.' + className + '["' + funcName + '"]');
Interceptor.attach(hook.implementation, {
onLeave: function(retval) { console.log("[*] Class Name: " + className);
console.log("[*] Method Name: " + funcName);
console.log("\t[-] Type of return value: " + typeof retval);
console.log("\t[-] Original Return Value: " + retval);
newretval = ptr("0x0")
retval.replace(newretval)
console.log("\t[-] New Return Value: " + newretval) } }); }
catch(err) { console.log("[!] Exception2: " + err.message); } }
else { console.log("Objective-C Runtime is not available!"); }
Once we run this, we see that the return value has been modified as shown below.
Patching function by modifying the return value in an iOS app – Jailbreak Detection bypass
If you look in your iOS application now, it would say that the Device is not Jailbroken (as shown below).
Jailbreak Detection Bypass – Device is Not Jailbroken
Conclusion
That is all for this blog post. In the upcoming blog post, we will look more into Frida Scripting and how you could leverage Frida’s API and additional tools to perform iOS and Android application security assessment.