Post iOS Jailbreak Customization: Exploring iOS Tweaks & Private APIs

Reading time: Reading time 7 minutes

If you have ever jailbroken an iOS device, you have likely encountered many things that we will discuss today. Let's start with one of the most commonly used terms that gets thrown around: iOS tweaks. There are plenty of them out there, depending on the needs - whether for fun or profit, simple or complex - ranging from making your lock screen look fancy to running your banking app on a jailbroken device.

For the sake of simplicity, we will just focus on building a little tweak that prevents a user from locking the device and unlocking it automatically, even if the device boots up into the locked screen. Tweak should trigger based on the state of the device instead of any external input such as screen touch, button press, etc.


Table of Content

 


A brief introduction to iOS tweak development

Tweaks can hook into the methods/functions of already existing application classes and tweak (literally, hence the name) their behavior. This enables you to modify the application's behavior without having access to its original source code. 

Tweaks can also add more features to the target application. To inject code into the existing application or process, a tweak requires a hooking framework such as cydia-substrate, fishhook, or ellekit.

 

How to develop tweaks?

To develop tweaks, one needs to have the development environment with build-essentials. Here, Theos comes into the picture, which is basically “A cross-platform build system for creating iOS, macOS, Linux, and Windows programs.”

Tweaks can be written in several languages, such as Objective-C, Swift, etc. 

If you use Swift or Objective-C for the tweak building, feel free to use XCode if that is your cup of tea. But here, we are going to use a preprocessor named Logos

Logos essentially acts as a boilerplate for the hooks we will generate for the ObjectiveC or the C classes. Here is a linter for Logos on the VSCode environment, which is handy during the tweak development.

Alright, now we have almost everything to get started with the tweak development, such as the basic idea of how tweaks work, what programming language we need to know to write a tweak, etc. 

The only things remaining are a macOS system and a jailbroken iOS device to replicate the steps listed below.

A step-by-step methodology for developing tweaks

The first step would be to set up the development environment by installing Theos on your system by following this guide. After that, install the SDKs for the devices that help us build tweaks. Since the iOS APIs change with the different versions, the methods and the API calls for a function that might be entirely different or even be removed in higher versions of iOS. 

If you plan to support a wide range of iOS versions, you must detect the version and perform tweak injections based on the available APIs. After you finish this step, we can get into the coding part. 

You should exercise patience here and be ready to go through many iterations of the tweak to avoid crashes and memory leaks, it is improbable that you will come up with an awesome tweak in a single go. 

Since we are touching the iOS private APIs that are not documented publicly by Apple, misusing them might cause some crashes and panic. Devices might get into “Safe Mode,” where you have the precious window of opportunity to unload your tweak (broken tweak) and debug it again with fixes.

iOS Jailbroken Device Safe Mode Pop-up

Safe Mode Popup


Gazing at the mountains of tweak development


Find the right classes and methods

We first need to identify the classes and methods that are responsible for the behavior or the feature we plan to modify. There are several resources available to take a look at the reversed headers for the iOS private APIs, you can do it yourself using tools such as Frida

Let’s explore both of these methods to see how they work. 

You might wonder, why would I write a tweak if I can do the exact same thing with Frida or Objection? 

The answer is that tweaks are much faster and can be injected into processes such as SpringBoard with persistence. Since there is an entire ecosystem to support tweak development, tweaks are built into a jailbroken device, and they provide more fine-grained control over the behavior manipulation of the applications. In many cases, tweaks are better than tools like Frida.

Tweaks can be shipped as IPAs or .deb files, which is also an advantage. Even though Frida Hooks can achieve persistence using the script mode, tweaks are better since there are ways to write UIs and toggle the tweaks, etc.

Now, we can start familiarizing ourselves with the iOS private APIs by looking at the symbols in the header files at Limneos. It is a great source to quickly refer to and check if we have anything that we can use to modify the program's behavior that we would like. 

For example, let us look at SpringBoard Framework, which manages many tasks, such as the home screen lock screen. You might ask how I know that SpringBoard is the framework that controls these tasks.

SBMesaUnlockTrigger.h Header File on developer.limneos.net

SBMesaUnlockTrigger.h

It is totally based on the trial-and-error method. Search for a known keyword on the site given above, in this case, the word “Unlock.” Choose the most relevant header and explore whether it might be responsible for the task. Here, we are narrowing down the Class that might be responsible for unlocking the iOS lock screen. 

We do see that several methods can be used to determine the state of the screen but nothing that can directly help us unlock iOS devices, So let's keep looking for it. 

We finally find a header named SBLockScreenManager.h, which contains the methods that we exactly need to unlock the UI. 

Two methods are startUIUnlockFromSource: and _finishUIUnlockFromSource:

SBLockScreenManager.h Header File on developer.limneos.net with interesting methods

SBLockScreenManager.h

Again, the methods are present inside the dumped header file from iOS 17.1. Make sure that they are also present in all the other versions of iOS so that the tweak will work on them as well. If not, just choose the target version while performing the search.

We are now done with the first step.

There are many ways to approach how the tweak works. When we want to unlock the device, instead of directly calling the methods responsible, we can trigger the unlock event using the device's home button via the APIs, which is equally valid.

Remember that there are several ways to do what we are doing now. Let’s stick with the method where we call the UIUnlock method.

Verify the findings with a sample tweak

Make sure that these methods we found in the private header files are being called during the lock screen unlock. 

We can do this in several ways as well. 

  • With Frida, we can hook into the class SBLockScreenManager, monitor the method calls, perform backtrace, and reverse its arguments to its methods. 
  • Or we can use a tool named Logify.pl. It helps us to generate the boilerplate template for all the methods and properties present inside a class, given that you provide the .h file, aka the header file. 

Do refer to the documentation of the logify tool and generate the tweak file (Tweak.x). You can also generate the logified file directly from limneos.net with the logify button.

SBLockScreenManager.h on Header File on developer.limneos.net with Logify button in focus.

https://developer.limneos.net/?ios=17.1&framework=SpringBoard&header=SBLockScreenManager.h


We can provide the
SBLockScreenManager.h file and get the logified methods. Logify creates templates with some preprocessors added such as %log, %orig etc. The %log function helps us to get enough information to observe the behavior of the class. 

Here we have to build the tweak. Let's get started using the New Instance Creator ( nic.pl )

Creation of a new Tweak project using new instance creator (nic.pl) tool by Theos.

nic.pl


Replace the Tweak.x file with the one generated by the command
logify.pl SBLockScreenManager.h > Tweak.x . We can observe that the target application (as bundle filter) to be injected is com.apple.springboard. 

Remember that we found these classes inside the SpringBoard Framework. If you choose a different application for example, com.apple.mobilesafari, the hooked methods will not be present and the hook will fail. 

Plist created with the name of the application is picked up by the TweakInjector during runtime as the configuration. This file acts as the filter file where the mentioned application inside is being considered as the target of the tweak injection.

It is recommended to remove the unrelated methods inside and a few methods which even fail to build without the interfaces within the tweak file. Alternatively you can include those extra methods as well by including the header files and update the makefile with the configuration $(TWEAK_NAME)_CFLAGS= -I<headerfiledirectory> as shown below. 

(Refer to the Theos documentation to understand the Makefile and its configuration)

A sample Makefile

A Sample Makefile

Tweak building

We can now proceed to build the tweak by using the command make package. Install the command using debian package manager (dpkg) after transferring it to the device. 

Or if you have set up the THEOS_DEVICE_IP environment variable to your iOS device’s IP Address (both your Mac and iOS device should be on the same network) and run make package install which will directly install tweak on the target device. 

After the installation springboard will reload. Let's start the console on your Mac and select the iOS device and set the filter to SBLockScreenManager, which is the name Class, and perform the unlock. We should be able to see the logs generated from the tweak within the console, and observe the sequence of the unlock task being logged. 

Now it's time to replicate these steps using the next iteration of the tweak.

Console logs from the dylib injected from the tweak.

Console logs from the dylib injected

Setting things in motion

To sum it up, till now we just tested out if the methods we found are triggered during the screen unlock. Since they are being triggered, we are gonna call these methods to trigger the screen unlock. 

But here comes one of the most important steps in the process: to build a tweak that acts independently without the need of any external triggers/inputs. Hence we have to either use some kind of a trigger system to call these methods as and when the screen gets locked or the phone boots up into the lockscreen.

One of the methods named screenOff from SBHomeButtonPressMesaUnlockTrigger.h gets called right after the screen turns off - the moment when the iPhone goes into the locked state. This is an ideal method to determine when to trigger these unlock methods. 

Let's look at how we are gonna write this using the Logos language. 

We will have to define a new method which will unlock the screen. Let’s call it unlock. If you look at the documentation of Theos, %new is used to add new methods to a class.

Now we will define a new method named unlock within the SBHomeButtonPressMesaUnlockTrigger class. The SBLockScreenManger.h can be imported or just the methods that are being instantiated here should be predefined as interfaces to avoid errors during compilation.

New "unlock" method created to unlock the iOS device

New unlock method


We have set the options based on the values
that were logged in the console, here the source is the home button denoted with number 13. 

The rest of the code follows the standard sharedInstance creation of methods from the respective class to invoke them with the necessary arguments. 

After this is done, we can call this newly defined method inside screenOff to trigger the unlock method as and when the screen turns off, [self unlock] is used to call the unlock method defined above.

Calling the "unlock" method inside the screenOff function

Unlock method call

init call includes the Group of all the hooked methods.
Constructor (entry point of the tweak)

 

The final touch

We are almost done, but let us make a toggle button for the tweak using the preferenceloader

iOS generally uses Plists as configuration files. For tweaks the preferences are stored at the path /var/mobile/Library/Preferences/com.example.tweak.plist

In case of rootless architecture the path is prepended with /var/jb .

The final version of the code here foreverUnlocked. Once installed it can be viewed from any package manager like Sileo or Cydia etc as shown below.

Tweak showcased in Sileo after installing from .deb package
Tweak showcased in Sileo after installing from a .deb package

Conclusion

We went through the process of building a simple tweak that does one thing only, that is to unlock the device without any prompt. If you find it insightful, try it out on your own. Explore the iOS private APIs and make the jailbroken iPhone more exciting.
Tweaks can be highly complex and do amazing things, feel free to explore the tweaks out there.

Happy hacking!!

Published on Jul 22, 2024
Samartha J V
Written by Samartha J V
Samartha is a security researcher at Appknox, known for his expertise in vulnerability assessment and penetration testing. With a passion for identifying and exploiting software vulnerabilities, he possesses a track record of dealing with complex security issues and delivering remediation strategies. Samartha's proficiency in communicating technical concepts to non-technical audiences, such as stakeholders and developers, is widely recognized. In his free time, he enjoys exploring new emerging technologies, playing CTFs, and delving into topics like cryptography, reverse engineering, and programming.

Questions?

Chat With Us

Using Other Product?

Switch to Appknox

2 Weeks Free Trial!

Get Started Now