The trick involves some (very basic) reverse engineering and is made up of several steps; I'll try to explain them as clearly as possible.
Step Zero: if the app is downloaded from the AppStore, it's encrypted. You have to decrypt it using one of the scripts/applications normally used to crack apps; one command line script is poedCrack.sh (google it, you'll find it quickly on one of the paste sites), one GUI application is Crakculous (it's available in Cydia). Note that one of these are needed for easy (automatic) decryption -- the manual decryption method is way too involved to put in a StackOverflow answer, that's why I'm suggesting these tools.) However, I don't in any way encourage you to crack apps! (Basically I'm asking you not to use these tools for their original purpose :) If you want to have a look at the manual decryption process, head here.
Step One: you need to do what classes the application uses/creates. For this, you need the class-dump or class-dump-z utility. This command-line application reverses the app's binary executable file and generates interface declarations for all Objective-C classes the app uses and has inside. You can find class-dump-z, the more advanced and preferred variant here.
Step Two: after you have the class declarations, you'll have to guess which class does what and when (yep, a bit confusing). For example, in one of the files generated from above app, Google Chrome, by class-dump-z, you may find something similar:
@interface ChromeUrlToolbar: UIToolbar {
UISearchBar *urlBar;
}
- (id)initWithFrame:(CGRect)frame;
- (void)loadURL:(NSURL *)url;
@end
Well, that sounds good, doesn't it? You can see that its implementation has an initWithFrame: method (as all UIView subclasses) -- why not try to modify it?
Step Three: for this modification, you'll need MobileSubstrate. MobileSubstrate is a developer library created by Saurik, the creator of Cydia, in order to make code injection to apps easy. You can find some really good tutorials on the web, including this one.
So, you've got a class and you wanna 'hook' it -- so you write some code like this:
static IMP __original_init; // A
id __modified_init(id __self, SEL __cmd, CGRect frame) // B
{
__self = __original_init(__self, __cmd, frame); // C
// D
UIButton *newButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[newButton setTitle:@"Chrome Pwned"];
newButton.frame = CGRectMake(0, 0, 100, 40);
[__self addSubview:newButton];
return __self;
}
// E
__attribute__((constructor))
void init()
{
Class clazz = objc_getClass("ChromeUrlToolbar"); // F
MSHookMessageEx(clazz, @selector(initWithFrame:), __modified_init, &__original_init); // G
}
Explanation: let's begin from the end. The init
function (E) is declared __attribute__((constructor))
. That means it's automatically called when the library we'll create out of this code will be loaded into Chrome. That's exactly what we want beause we want to alter our application's behavior prior to having started it.
On the line marked // F
, we capture the class object itself we want to modify. Objective-C is a highly dynamic language; that means we can get and modify information about the classes and objects at runtime. On the line marked // G
, we use the most important function of the MobileSubstrate API: MSHookMessageEx. To understand how it works (rather what it does), you must know the following: Objective-C itself is implemented as a plain C library -- the language itself, under the hoods, is just simple C. So every message send in Obejctive-C is actually a C function call. These C function have two special arguments: self
and cmd
-- the former is the pointer to the object being messaged, the latter is the selector (a special, unique pointer to the name of the message being sent). So what MSHookMessageEx does is it takes a class and a selector, finds the implementation of the function corresponding them, and exchanges that function with the function supplied in its 3rd argument itself (__modified_init
in this case). In order not to lose data, it also returns the function in its 4th parameter (here it's __original_init
).
So, now the initialization of the Chrome URL toolbar is redirected to our function, what to do next? Well, nothing special: first we just call the original initialization function (notice the first two special arguments, __self and __cmd!) which creates the toolbar as if normally (this line of code is denoted by // C
). Then, we do the actual alteration: in section // D
, we create an UIButton, set its title and place, and add as a subview to our freshly created toolbar. Then, knowing this is an initalization function, we return back the original instance along with our button's code injected into it.
Well, that's basically what you'll need to know about this; if you're interested in deeper details of how Objective-C works and how you can create cool iOS tweaks, I suggest you to read Apple's official documentation on the topic and you can browse through some of my opensource Cydia tweaks. as well.
I hope this will help you!