Bishop Fox named “Leader” in 2024 GigaOm Radar for Attack Surface Management. Read the Report ›

Rethinking & Repackaging iOS Apps: Part 2

Iphone screen with home screen apps Rethinking & Repackaging iOS Apps: Part 2

Share

In the first part of our series, we looked at how to modify an iOS application binary by inserting load commands to inject custom dynamic libraries. In Part 2, we take this a step further by introducing a toolchain designed to make some of our favorite iOS application hacking tools available on non-jailbroken devices.

To facilitate this, we forked the fantastic Theos project by DHowett. For the uninitiated, Theos is basically a build environment that allows you to (among other things) easily write, build, and deploy Cydia Substrate tweaks for apps on jailbroken devices. Theos takes care of all the cross-compiling, linking, etc., leaving you free to write the code you want without any fuss.

Until now, Theos only worked on jailbroken devices because it requires additional frameworks and libraries to be installed on your iOS device, as well as requiring a modification to the operating system to inject dynamic libraries into applications at runtime. We have circumvented this by including all of the required frameworks and libraries; our approach also involves modifying application binaries instead of the operating system. Full details of how this was done will be released later, but until then, let’s concentrate on how to use this thing.

We’ll run through an example of how to patch an app by modifying the Trulia house-hunting app to spoof our location, so that our device always appears to be located inside the White House in Washington, D.C.

Getting Started

First, check out the code from GitHub. I installed it into ~/Projects/theos-jailed on my laptop. From there, create a new tweak just as you normally would with Theos:

carl@europa ~/Projects> ~/Projects/theos-jailed/bin/nic.pl

NIC 2.0 - New Instance Creator

------------------------------

  [1.] iphone/application

  [2.] iphone/library

  [3.] iphone/preference_bundle

  [4.] iphone/tool

  [5.] iphone/tweak

Choose a Template (required): 5

Project Name (required): Trulia

Package Name [com.yourcompany.trulia]: com.bishopfox.trulia

Author/Maintainer Name [Carl]:

[iphone/tweak] MobileSubstrate Bundle filter [com.apple.springboard]:

[iphone/tweak] List of applications to terminate upon installation (space-separated, '-' for none) [SpringBoard]:

Instantiating iphone/tweak in trulia/...

Done.

Upon completion, Theos-jailed creates a Trulia directory inside the current directory, which in this case is ~/Projects/trulia.

Extracting the App

To extract the Trulia app from the iPhone it’s installed on, we use the iFunBox application. Right click on Trulia, then choose “Backup to .ipa package."

Screenshot1

We save the .ipa file in ~/Projects/trulia and rename it trulia.ipa.

carl@europa ~/Projects> cd trulia/

carl@europa ~/P/trulia> ls -l

total 68720

drwxr-xr-x  4 carl  staff       136 Mar 25 22:37 Cycript.framework

-rw-r--r--  1 carl  staff       253 Mar 25 22:37 Makefile

drwxr-xr-x  4 carl  staff       136 Mar 25 22:37 PatchApp

-rw-r--r--  1 carl  staff        57 Mar 25 22:37 Trulia.plist

-rw-r--r--  1 carl  staff      1703 Mar 25 22:37 Tweak.xm

-rw-r--r--  1 carl  staff  35148819 Mar 25 22:39 [Trulia][i-funbox.com].ipa

-rw-r--r--  1 carl  staff       203 Mar 25 22:37 control

drwxr-xr-x  6 carl  staff       204 Mar 25 22:37 fishhook

-rwxr-xr-x  1 carl  staff     10718 Mar 25 22:37 patchapp.sh

lrwxr-xr-x  1 carl  staff        33 Mar 25 22:37 theos -> /Users/carl/Projects/theos-jailed

carl@europa ~/P/trulia> mv [Trulia\]\[i-funbox.com\].ipa trulia.ipa

Uninstalling the App

Due to namespace collisions, you can’t reinstall a patched app over the original app. You have to uninstall the original first. You can either do this on your device or do it with iFunBox, as shown below:

Decrypting the App

Apps are encrypted on Apple devices. To patch an app, it is necessary to decrypt it; however, this step can – at present – only be done on a jailbroken device. Thus, we copy the Trulia .ipa file to a jailbroken iPad Mini; again, we use iFunBox to do this:

Hit “Install App” and choose the .ipa file you previously saved. In a few seconds, the app should be installed on the jailbroken device. Ensure that you have installed the Clutch tool, which we will use for decrypting the Trulia app. Now, SSH into your device and run clutch Trulia to decrypt Trulia and save a new .ipa file containing the decrypted app ( Note: We’re using port redirection to connect 127.0.0.1:33333 to the ipad:22):

carl@europa ~/P/trulia> ssh root@localhost -p 33333

Kraken:~ root# clutch Trulia

…

DEBUG | Cracker.m:360 | Saved cracked app info!

    /User/Documents/Cracked/Trulia-v416-Foo-(Clutch-1.4.7).ipa

elapsed time: 6.96s

Applications cracked:

Trulia

Total success: 1 Total failed: 0

Let’s rename it to something more user-friendly:

Kraken:~ root# cd /User/Documents/Cracked/

Kraken:/User/Documents/Cracked root# mv Trulia-v416-Foo-(Clutch-1.4.7).ipa trulia-cracked.ipa

Finally, back on our laptop, we copy the decrypted .ipa from the jailbroken iPad into our ~/Projects/trulia/ folder:

carl@europa ~/P/trulia> scp -P 33333 "root@localhost:/User/Documents/Cracked/trulia-cracked.ipa" ./trulia.ipa

Success! We now have a fully decrypted, hackable version of the Trulia app in trulia.ipa.

Writing a Tweak

Remember: Our intention is to spoof our location to the Trulia app. To do this, we have to hook/swizzle some of the Apple CLLocationManager framework methods, specifically the class that implements the CLLocationManagerDelegate protocol. The process of doing this is exactly the same for Theos-jailed as it is for a regular Theos tweak – you can use %hook, %end, %ctor, etc. as normal.

We open up ~/Projects/trulia/Tweak.xm and insert this code:

1.    #import <dlfcn.h>

2.    #import "Cycript.framework/headers/cycript.h"

3.    #import <CoreLocation/CoreLocation.h>

4.    #include "fishhook/fishhook.h"

5.    #include <objc/message.h>

6.

7.    #define CYCRIPT_PORT      31337

8.    #define SPOOFED_ALTITUDE  0.00

9.    #define SPOOFED_LATITUDE  38.89876

10.    #define SPOOFED_LONGITUDE -77.036679 // The Whitehouse

11.

12.    %ctor {

13.        // Start Cycript

14.        CYListenServer(31337);

15.        NSLog(@"Injected into Trulia :)");

16.    }

17.

18.    void spoof_updateLocations(id cls, SEL selector, CLLocationManager *locationManager, NSArray *locations) {

19.        static CLLocationCoordinate2D spoofedCoords;

20.

21.        spoofedCoords.latitude=SPOOFED_LATITUDE;

22.        spoofedCoords.longitude=SPOOFED_LONGITUDE;

23.

24.        CLLocation *realLocation = [locations lastObject];

25.        CLLocation *spoofedLocation = [[CLLocation alloc]   initWithCoordinate:spoofedCoords

26.                                                            altitude:SPOOFED_ALTITUDE

27.                                                            horizontalAccuracy:[realLocation horizontalAccuracy]

28.                                                            verticalAccuracy:[realLocation verticalAccuracy]

29.                                                            course:[realLocation course]

30.                                                            speed:[realLocation speed]

31.                                                            timestamp:[realLocation timestamp]];

32.        if([cls respondsToSelector:@selector(locationManager:oldDidUpdateLocations:)]) {

33.            [cls performSelector:@selector(locationManager:oldDidUpdateLocations:) withObject:locationManager withObject:@[spoofedLocation]];

34.        }

35.    }

36.

37.    %hook CLLocationManager

38.    -(void)setDelegate:(id)delegate {

39.        IMP old_updateLocationsMethod = class_replaceMethod(object_getClass(delegate), @selector(locationManager:didUpdateLocations:), (IMP)spoof_updateLocations, "@:@@");

40.        class_addMethod(object_getClass(delegate), @selector(locationManager:oldDidUpdateLocations:), old_updateLocationsMethod, "@:@@");

41.        %orig;

42.    }

43.    %end

The code replaces –[CLLocationManager setDelegate:] with a new version that ensures all callbacks to the delegate method locationManager:didUpdateLocations: are intercepted, modified with new coordinates, and then passed to the original delegate method.

To build the tweak, simply run “make:"

carl@europa ~/P/trulia> make

/Users/carl/Projects/trulia/theos/makefiles/targets/Darwin/iphone.mk:41: Deploying to iOS 3.0 while building for 6.0 will generate armv7-only binaries.

Making all for tweak Trulia...

 Preprocessing Tweak.xm...

 Compiling Tweak.xm...

 Compiling fishhook/fishhook.c...

 Linking tweak Trulia...

 Stripping Trulia...

 Signing Trulia...

Upon completion, we will have a new shared library called Trulia.dylib that we will eventually inject into the Trulia app.

But first, we must prepare our provisioning profile.

Preparing a Provisioning Profile – The App ID

As any iOS developer knows, installing an app onto an iOS device requires a matching provisioning profile that contains a lot of information pertaining to developer certificates, app entitlements, etc. It can get pretty hairy, but we’ve tried to make this part of the process as easy as possible.

First, you need to know the correct entitlements for the Trulia app. Entitlements are essentially permissions given to the app by you, the developer. You can grant entitlements for things like Apple Pay, push notifications, etc. Our version of Theos-patched comes with a tool that will tell you the entitlements required to repackage any given app. To run it, just do this:

carl@europa ~/P/trulia> ./patchapp.sh info trulia.ipa

[+] Unpacking the .ipa file (/Users/carl/Projects/trulia/trulia.ipa)...

…

===========

= Summary =

===========

Do all of the things mentioned under "What to do now", above.

Make sure that the provisioning profile contains the correct entitlements (see above).

Once you've installed the provisioning profile on your device, run the following command:

    ./patchapp.sh patch trulia.ipa /path/to/your/file.mobileprovision

Bundle ID: com.trulia.Trulia-patched
Required Entitlements:

In this case, there are no special entitlements required for Trulia to operate. You’ll also see a “Bundle ID." This is important and you’ll need it later. For now though, sign into the Apple Developer Member Center and select “Certificates, Identifiers & Profiles”:

From here, select “Identifiers:"

You need to add a new App ID. When prompted for the “Bundle ID,” enter the value provided by the “patchapp.sh info …” command. In this case, it’s “com.trulia.Trulia-patched:" 

You’ll be prompted to enter “App Services." These are actually the entitlements mentioned earlier. In the case of Trulia, we don’t need any additional entitlements, so it’s safe to leave all of these options unchecked:

Follow the wizard to the end. You’ll get there eventually; you can see I called this App ID “Trulia Patched:"

ADDLScreenshot

Preparing a Provisioning Profile: The Profile

Once you’ve generated an App ID, you can generate a matching provisioning profile. Select the menu option to do so, then add a new profile:

You’re going to use an “iOS App Development” profile:

You’ll be asked to choose a corresponding “App ID." Choose the one you just created:

You’ll be asked to select the certificates used for signing. Choose your cert – I only have one:

Image4

You’ll also be asked to select the devices to which you’d like to install the provisioning profile. I chose all of mine – seven in total. Finally, you’ll be asked to give the profile a name. I called mine “Trulia Patched Profile:"

Great success! It worked:

Hit “Download” and copy the .mobileprovision file into your project folder, which in our case is ~/Projects/trulia/:

Now, we’re ready to install it on a device.

Installing the Provisioning Profile

You can use Xcode to install a provisioning profile onto your non-jailbroken device. Go to Window/Devices and right click on your device:

You’ll be shown a list of profiles already installed on the device. Click the + button to add your newly generated profile:

Choose the Trulia .mobileprovision file and it will be installed for you:

Patching the IPA File

Go ahead and inject the Trulia.dylib file (and accompanying libraries) into the Trulia app. The patchapp.sh tool automates the entire process. Simply run:

carl@europa ~/P/trulia> ./patchapp.sh patch trulia.ipa Trulia_Patched_Profile.mobileprovision

[+] Unpacking the .ipa file (/Users/carl/Projects/trulia/trulia.ipa)...

[+] Copying .dylib dependences into ".patchapp.cache/Payload/Trulia.app"

[+] Codesigning .dylib dependencies with certificate "iPhone Developer: CARL LIVITT (XXXXXXXXXX)"

     .patchapp.cache/Payload/Trulia.app/Trulia.dylib

     .patchapp.cache/Payload/Trulia.app/CydiaSubstrate

     .patchapp.cache/Payload/Trulia.app/ap.dylib

     .patchapp.cache/Payload/Trulia.app/cy.dylib

     .patchapp.cache/Payload/Trulia.app/readlin.dylib

     .patchapp.cache/Payload/Trulia.app/ncur.dylib

     .patchapp.cache/Payload/Trulia.app/cycript

     obj/Trulia.dylib

[+] Patching ".patchapp.cache/Payload/Trulia.app/Trulia" to load "Trulia.dylib"

[+] Generating entitlements.xml for distribution ID XXXXXXXXXX

[+] Codesigning Plugins and Frameworks with certificate "iPhone Developer: CARL LIVITT (XXXXXXXXXX)"

ls: .patchapp.cache/Payload/Trulia.app/PlugIns/com.*/com.*: No such file or directory

ls: .patchapp.cache/Payload/Trulia.app/PlugIns/com.*: No such file or directory

ls: .patchapp.cache/Payload/Trulia.app/Frameworks/*: No such file or directory

[+] Codesigning the patched .app bundle with certificate "iPhone Developer: CARL LIVITT (XXXXXXXXXX)"

     Trulia.app: replacing existing signature

[+] Repacking the .ipa

[+] Wrote "trulia-patched.ipa"

[+] Great success!

That’s it! The tool inserts the relevant load command, adds your Cydia Substrate tweak, performs code signing using your developer certificate, and rebuilds the .ipa file as trulia-patched.ipa.

Installing the Patched App

The last step is to use Xcode to install the patched app back onto the iOS device. For this, we use the device manager again:

When prompted, choose the newly generated trulia-patched.ipa file. Xcode will install it onto the device. You should see an entry like this in the Xcode console when the installation is finished:

Mar 25 23:23:49 iPhwn SpringBoard[43] <Warning>: Installed apps did change.

    Added: {(

        "com.trulia.Trulia"

    )}

    Removed: {(

    )}

    Modified: {(

    )}

The devices window will also show that Trulia is installed:

You should now be able to launch your app. If it crashes and you see this in the Xcode console, it means you forgot to decrypt the app using Clutch:

kernel[0] <Notice>: AppleFairplayTextCrypterSession::fairplayOpen() failed, error -42022

Just go back to the beginning and use Clutch to decrypt the app using a jailbroken device.

Testing the Spoofed Location

Everything should happen like magic. If the patch was successful, this is where Trulia will think it is:

Taking It Further: Cycript

We promised Cycript, and it’s enabled by default in Theos-jailed. Any time you run your app, a Cycript server is automatically instantiated on port 31337/TCP of the device. To connect to it, you will need the Cycript tools installed on your MacBook (download them here). After that:

carl@europa ~/P/trulia> ~/bin/cycript -r 192.168.1.100:31337

cy# UIApp

#"<UIApplication: 0x146833e0>"

Here you see us passing the -r option to Cycript, which specifies the IP address of the target iPhone and port 31337. From here, Cycript can be used as normal. For example, let’s enumerate the view controllers on screen:

cy# ?expand

expand == true

cy# UIApp.keyWindow.recursiveDescription

…

<UILabel: 0x1856ba30; frame = (28.75 6.74365; 142.5 24.5127); text = 'Washington, DC'; autoresize = W; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x1856bc00>>

…

The output has been truncated for readability, but above we can see the UILabel object corresponding to the following label on the app’s UI:

We can easily change it:

cy# label = new Instance(0x1856ba30)

#"<UILabel: 0x1856ba30; frame = (28.75 6.74365; 142.5 24.5127); text = 'Washington, DC'; autoresize = W; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x1856bc00>>"

cy# [label setText:@"Bishop Fox!"]

The change is immediately reflected in the UI:

All of your normal Cycript tricks should work just fine. Have fun!

That’s All (For Now) Folks!

There’s a lot going on under the hood to make this happen. We also implemented the Fishhook library to dynamically rebind function symbols within iOS apps. This gives us functionality equivalent to MSHookFunction. More documentation will follow on that soon.

Subscribe to Bishop Fox's Security Blog

Be first to learn about latest tools, advisories, and findings.


Carl Livitt

About the author, Carl Livitt

Bishop Fox Alumnus

Carl Livitt is a Bishop Fox alumnus. He was a Principal Researcher at Bishop Fox with decades of experience in mobile and application security, hardware and embedded devices, reverse engineering, and global-scale penetration testing.

Carl is credited with the discovery of many vulnerabilities within both commercial and open-source software. He was brought in as a third-party expert to lead the team that confirmed several security issues with St. Jude Medical implantable devices. His work eventually led to an official communication from the FDA.

Carl has served as a contributing author to Hacking Exposed Web Applications 3rd Edition as well as a technical advisor for Network Security Assessment 1st Edition. He has been interviewed on NPR and quoted in publications including USA Today and eWeek. Carl co-authored the iOS reverse engineering framework iSpy, which was featured at Black Hat USA's Tools Arsenal.

More by Carl

This site uses cookies to provide you with a great user experience. By continuing to use our website, you consent to the use of cookies. To find out more about the cookies we use, please see our Privacy Policy.