An updated version of this tutorial, for iOS 7 and above, can be found on my personal website￼.
Welcome to my second tutorial on iOS Open Development! In this tutorial, I will give you the first hand to get started with MobileSubstrate tweaks. If you are here, you should already know what MobileSubstrate is and why you would want to write tweaks for it, so I’m not going to get into too many details about MobileSubstrate and how it works.
Before we get started, there’s a couple of things I want to say first. First, if you don’t know what you are doing (new programmer, first time writing for iOS, whatever), then this is NOT a tutorial for you. MobileSubstrate protects the system from tweak errors and lousy programmers, but one can never be too careful. If you don’t know what you are doing, you could mess up your device, perhaps not bad enough to brick it, but bad enough to require a full restore. So if you follow this tutorial, I trust you know programming, have been doing it for a while, and that you take full responsibility of whatever happens to your device for writing “bad” tweaks.
Second, writing MobileSubstrate tweaks… There’s a lot you can do. It’s all about code hooking thanks to Objective-C’s dynamic nature and there’s a lot you can do with it. I cannot possibly teach you, not even in a thousand posts, everything you can modify with MobileSubstrate tweaks. This small tutorial is just meant to give you a hand getting started with tweaks. It’s not a comprehensive guide. It would really be impossible to write a full book about everything you could hook because there’s too much. Once you are finished with this guide, you will know enough about widgets and you will be able to research on your own: That is, once you are done with this tutorial, you will have to explore the private headers to find what you are looking for and hook it. Writing MobileSubstrate tweaks requires patience and familiarization with the private frameworks, so once you build your first project, start studying the private frameworks: I will tell you how to dump your own headers or where you can find headers that have already been dumped and modified for you to use.
Now, putting that aside…
In this tutorial you are going to write your first MobileSubstrate tweak. We will build a simple tweak that will show the user a message whenever he/she tries to open an app. Once the user taps an icon, we will display a message that says “Launching App…” . Yes, it is a useless tweak, but you will learn a lot in your way.
In this tutorial, I will do the following:
- I will explain you how to get the required headers to create tweaks.
- I will explain what “hooking” code is in a nutshell
- I will teach you how to hook code using Logos instead of the Objective-C runtime, so you will learn the basic Logos syntax along the way and you will be ready to hook your own things any time.
You will learn three very basic things but they should be powerful enough for you to write any tweak you can think of.
Okay, let’s get started!
First Things First: You Need The Private Headers To Work With.
You need to get the private iOS headers you want to “hook”. Otherwise, well, there’s nothing you can play with. So I will show you ways to do that.
The first and hardest method is to dump your own headers yourself using a command line tool called class-dump-z (or class-dump: refer to this post for more details on the tools). How to use this tools is beyond the scope of this tutorial but a word on them is due. Dumping your own headers is a tedious option. There’s a bunch of private frameworks and it can take a while to dump them all. Save each header to a respective framework directory when done.
Another (and easier) option is to download the headers from someone else. A lot of people have dumped their own headers and are hosting them free of charge on GitHub, so go on there and grab a copy. Personally, I recommend you use rpetrich’s headers, simply because he is a great developer with great reputation in the iOS open development community. But there’s lots of options out there shall you want/need them.
Once you have your headers downloaded, each header nicely inside a folder with the name of the framework, move or copy each framework to the directory called “include” inside the Theos directory. By default, you would save all the headers in the following location (Mac):
Every private framework you download has to be saved there (so you can simple #import them when you want to use them, as you would include any other iOS framework in “official”, non-jailbreak projects).
That is all there is to downloading your headers: Once you are done, you can move on.
But Wait, What Exactly Is “Hooking”?
Objective-C is just a superset of C. Everyone who knows iOS or Mac programming knows that, and it’s the first thing a good book or tutorial on Objective-C tells you. But what exactly does this mean? It simply means that Objective-C has a lot of C code running under the hood. As so, you can freely mix any C code with Objective-C code.
Objective-C is mainly driven by what is called the Objective-C Runtime. The Objective-C Runtime is a C framework that, as you may imagine, controls everything that happens on Ojective-C under the hood. This runtime is responsible of creating classes, creating methods, ivars… Everything Objective-C does, is controlled by this run time. This runtime is in its most superficial level a bunch of C functions, and this framework is the reason Objective-C is such a dynamic language. This runtime can create classes, methods, ivars, and modify any of them along way on runtime rather than on compile time. Here are some functions provided by this framework:
This are just some functions found in the runtime. Their purpose is really clear: The first one adds an ivar to an existing class. The second one adds a method to an existing class, and the third one sends a message to a class.
Programmers can have full access to the Objective-C Runtime functions by just importing the framework as they would import anything else, granting them access to very powerful tools to modify running code as they wish on runtime and have a lot of control of whatever happens in their machine. In a running application, a developer can “hook” to a current method and make it behave like something completely to what was intended. In other words, to “hook” means to modify or extends the Objective-C code to do what you want. For example, someone could “hook” the NSString’s stringByAppendingString to append the passed parameter and to append a personal string at the end. Suppose I wanted that method to append “Leo” at the end of very string that receives that message. I could hook that method to append the wanted string and my own personal string at the end.
The Objective-C Runtime takes care of hooking. Writing tweaks require hooking, but the iOS Open development community has developed methods to abstract the Objective-C Runtime functions from the programmer. “Hooking” mechanisms have been created precisely to abstract the runtime and perhaps make it less intimidating. CaptainHook is one of them, and Logos is another one. Logos is a very nice hooking mechanism and it abstracts the runtime nicely with simple commands like “%hook” and “%orig”. No idea what I am talking about? In this tutorial I will show you how to create a tweak with Logos, so you will understand more once you see the actual tweak.
Without further ado, time to build the actual tweak and get your hands dirty. This is actually the shorter part of this tutorial but I hope everything I taught you so far will be useful for you in the future.
Getting Our Hands Dirty: Creating The Tweak
Alright, launch up the terminal and create a MobileSubstrate project (If this is your first time using Theos, or you don’t even have Theos installed yet, refer to this post. I mainly give links to instructions for installing Theos but the small content I dedicate to installation and configuring should help you getting started with it). I will call mine LaunchNotifier:
Once you have your project created, you will see a file called “Tweak.xm”. This is the file that has all the Logos hooking code and where you write your tweak. So open it up and be surprised for a short while at all that “%” syntax…
You also need to modify the makefile to add the UIKit framework. If you are in this tutorial you should know how to do that, but if you don’t, it’s very simply, just add this line:
1 LaunchNotifier_FRAMEWORKS = UIKit
And save it. That’s everything we need to do so close it now.
You will see a bunch of commented-out content. Please look at it for a while and try to understand it. It shows you the very basics of code hooking like:
- %hook ClassName and %end. This is what I call a “Hook Block”. Everything you write in between is the block where you will be hooking things of the className (note that %hook and %end have no finishing semi-colon in their lines).
- %orig calls the original version of the code you are hooking. You will find yourself calling %orig in all your methods most of the time, as they have most of the code needed for the code to be finished. Not calling %orig; can have disastrous consequences in your tweak, depending on what you are doing, although sometimes it’s desirable to avoid calling the original version of the method. Make sure you know when you want to call orig and when you don’t. Usually, it’s just common sense.
- %orig(…) is %orig; but with arguments. In the parenthesis you pass in all the parameters to hand them over to the original method.
The rest of the commented code are examples for Logos. They can work, but in this tutorial I want to build something that works, because it’s easier to learn things with “real-world” examples.
Feel free to delete everything on that file for now. We will build everything from scratch (and it’s nothing too big, since it’s just a UIAlertView basically).
First things first, a quick review of what we are doing to do: When the user taps an icon in the SpringBoard, he will see a message telling him he’s about to launch an app.
Okay, so how do we do that? I need to say again, Tweak development requires lots of header browsing. You can never be certain of where things are. You know you want to a show message when an icon is tapped. You know icons are part of SpringBoard, so that’s a good start. You would head over to the SpringBoard framework folder and browse all the headers there. What are you looking for? Common sense is to be used here: You know, it’s an icon, so just look for anything that may have “Icon” on it’s name. What should you hook? You browse your headers for a while and find a class called SBIcon. Is this the one? Could be. Under normal circumstances, you would try to hook different classes and methods, writing code for them to do something (show an UIAlertView is a good idea), to see if you are hooking the right class and method. Okay, nothing interesting here. This can’t be the method. Oh, oh, what do I See? Is that a class called SBApplicationIcon? That sounds less generic and it may be exactly what you are looking for! In our case, yes, we want to hook that class.
You inspect that class and you see it has a method called -launch. Instinctively, we know this method will launch the respective app, so it makes sense to hook it.
The initial hooking will simply look like this:
//Methods to hook here.
Here we are just saying “I will hook some methods from the SBApplicationIcon class”.
-launch is the method that holds the magic for us, so to hook it, just write it’s implementation inside the hook block like you would anywhere else:
//Hooked launch. What will it do now?
And there you can just write any Objective-C code you want. We want to show a message, so just write a UIAlertView like this:
NSString *appName = [self displayName];
NSString *message = [NSString stringWithFormat:@"The app %@ has been launched", appName, nil];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:appName message:message delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
And that’s really it! make package install your project (you should now that by now). Once the user taps an icon, the app will launch while showing a message:
A couple of things you have to note about the code we wrote above:
- We are calling %orig;. If you don’t call %orig; here, your app won’t launch because you will override all the launch logic. Instead, you will just see your message but nothing will be launched. If a normal user installed this app (without the method calling %orig;), he/she would be screwed. Why? Because he/she wouldn’t be able to launch Cydia and uninstall it. The developer, on the other hand, can reverse the problem by simply fixing the problem and make package installing it again. This is why tweak programming is not for everyone and you should only do it if you plan on doing it well.
- Note that we can call [self displayName]. Like I said, you can write any code like you normally code as you usually would, except you can “call hooks” whenever you want or need them.
Congratulations! You have now written your first MobileSubstrate tweak. As usual, the full code can be found on my pastebin here, and if you have any questions, head over this post and ask all the questions you may have (or, use comments section below).