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.
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.
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
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
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.
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 )
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
(Refer to the Theos documentation to understand the Makefile and its configuration)
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
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
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.
Unlock method call
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 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!!
Discussion about this post