Do you own an iPhone or an iPad? If so have you ever considered jailbreaking it? Probably not, as according to CodeProof only about 10% of devices are jailbroken. This is partly because jailbreaking has a bit of an image problem - seen as a set of instructions that one can follow to enable app piracy - partly because Apple keeps blocking jailbreaking methods, probably in an effort to counteract said app piracy.
Although I'd estimate that the majority who jailbreak do use it for the piracy, that's not why I do it (You can pirate apps without jailbreaking by signing them with a developer account, purchased directly from Apple, anyway). Jailbreaking's best feature is that it enables one to run code without needing Apple to tell you that you're allowed. Without jailbreaking, running native code on the iOS device you bought, paid for, and own will set you back about £60 a year. Jailbreaking gives you freedom.
Another great feature is that by using Saurik's incredible Cydia Substrate you can modify code that already exists on your iDevice. One can inject arbitrary modifications to the system code - or modify apps downloaded from the App Store. In the jailbreak world these modifications are known as Tweaks and are often shared with other jailbreakers - either for free or for a small price - through Cydia, a sort of App Store for jailbroken devices.
I want to give you a general overview of the process of developing a (very) simple tweak, to give you an idea of what playing with this stuff is like.
Written by Jay Freeman (saurik) of Cydia fame, what it does is give us an interactive and immediate way to modify running processes on iOS.
If we SSH into an iOS device with cycript installed, we can run it directly from the device. This immediately gives us access to a REPL environment set up and ready to play with. It's at this point we can also decide what process to inject our modifications into.
Above I have injected into the Springboard process. In iOS, this process controls most of what you will be seeing when you're not in an app. It helps control everything from the lock screen to the app switcher.
Now before we can change anything, we need to know what we want to change, it's name and where it is. The public stuff - that is, the tools people use to write iOS apps in Objective C - is documented and easily found on Apple's developer website. The problem is that Apple has lots of code where the stuff we are interesting in changing are all undocumented.
Luckily, in Objective C there is a way to extract classes, instance variables, properties and method names from the compiled binaries, without needing source code. People have done this for most versions of iOS and so big collections of so called class headers can be found on Github. As an example I have just searched for "ios headers" on Google and this was the first hit.
Anyway, back to what we are going to change for our simple tweak. For the sake of example let's say we want to change the boring old No Notifications label to say something a little more interesting!
There are several ways to find something that we want to change using cycript. One way is to use the header dumps I mentioned before to interact with classes, methods and variables directly by typing in their names. Another method - useful if one is modifying an app rather than Springboard - is to call
UIApp.keyWindow.recursiveDescription which will print a hierarchal description of the screen's configuration right now. You can work backwards from the bottom of that description to eventually find the class you want to change.
The method I will use is to invoke a handy little function built in to cycript called
choose. The choose function searches the injected process' memory for any classes matching what you ask for, then grabs them all as an array.
Here I have asked for all the instances of the
UILabel class, assuming that our No Notifications message will be a
UILabel. Due to the sheer number of
UILabel in memory, I found it easier to set it up so that cycript shows just the text of the labels. Luckily, due to the script-y nature of cycript (Oh - I get it!) you can do this in one line
[choose(UILabel)[i].text for(i in choose(UILabel))]
And finally to pick out the UILabel we want and save it, simply check for the text we are looking for!
for(i in choose(UILabel)) if (choose(UILabel)[i].text == "No Notifications") nnLabel = choose(UILabel)[i];
In the above snippet, we've saved the
UILabel that's text matched "No Notifications" as
nnLabel. Now we can interact with the label as much as we want, and even call all the usual methods one can use on a
UILabel. An interesting looking method is
setText, both documented in class headers found by Googling UILabel.h and also found in Apple's own developer docs.
[nnLabel setText:@"S'all Good"].
Unfortunately, this little memory hacking change isn't permanent, closing and reopening the notification center will cause Apple's original code to rerun and ruin all our hard work. Making the tweak permanent will be the focus of a future post describing how one can hook into Apple's code programmatically to change what is actually run, rather than fiddling about with the memory manually.
In future parts 2 and 3:
- Theos/Logos and Hooking - making changes permanent
- Debs, Repos and the Cydia store - distributing a tweak