This is the second part of the "Reverse Engineering iOS Apps - iOS 11 Edition" series. In the first part of the series we learned how to setup your phone on iOS 11 and how to decrypt an iOS app. In this second and final part we'll learn how to:

  • Dump Starbucks app's classes using class-dump
  • Disassemble the Starbucks app using Hopper
  • Runtime manipulation using bfinject cycript

Dump Starbucks app's classes using class-dump

One of the best ways to understand the behaviour of an iOS app is to read the interfaces exposed when dumping it's classes. As you probably know by now, iOS apps are no longer exclusively written in Objective-C, they now can also be written (and Apple is encouraging developers to do so) in Swift or a mix of both. This means the tools we used before to dump iOS apps classes won't work when there is Swift code in the binary, because of this I have 2 versions of class-dump. You can download them from here (Objc class-dump) and here (Swift class-dump). The Starbucks app has swift code in it so you'll have to use the Swift version of class-dump.

  • Download the Swift version of class-dump, move it to /usr/local/bin (this is to be able to execute it from any folder in the system) and give it execution permissions.
> cd ~/Downloads
> mv class-dump-swift /usr/local/bin
> chmod +x /usr/local/bin/class-dump-swift
  • Change directories to the folder where you have the Starbucks App binary you extracted following my previous post.
> cd /path/to/Starbucks.app/
  • Run class-dump-swift on the binary and save the output in a file.
> class-dump-swift Starbucks > ~/Downloads/starbucks-dump.txt
  • You can open the starbucks-dump.txt file in any text editor and you'll see most of the classes, methods and even instance variables. There might be some missing information because class-dump couldn't parse it.
Starbucks app class dump

Disassemble the Starbucks app using Hopper

Having the name of the classes, methods and instance variables is a great way to start RE an iOS app, but we are still missing the core of the application, its business logic. We need to understand what is happening inside the methods? what are the rules that the application follows? and to do this we'll need a disassembler. I've been using Hopper Disassembler for a few years now and I love it, it's perfect for beginners like me. You can download a trial version here, it gives you 30min sessions but you cannot save your work. The good news is that the full version costs only $99 for a personal license (unlike its more expensive friend that costs thousands of dollars, because it's aimed at more experienced users and has many, many more features).

  • Download, install and open the Hopper Disassembler.
  • Press CMD (⌘) + O and locate the Starbucks.app/Starbucks binary
  • Let Hopper process the mach-o file format information of the binary and disassemble the binary data.

We could spend hours and hours going through the binary, so I'll let you play around with it from here. In another post I'll show you how using Hopper you can modify (patch) a binary and generate a new one, then repackage it and install it back on a device. But I'm going to give you an example.

  • Search the term "fakeCardOfType" in Hopper and scroll down in the assembly view, you'll see the following:
  • This is a class method on the SBXCard class that creates a fake Starbucks card of type "US", "CA" or "UK" and fills all the required fields with dummy data. Click on the Pseudo-code mode button on the top bar and you'll get, as the name suggests, a pseudo-code view of the assembly:
  • Continue investigating classes that you think are important to understand the behaviour of the application. What if I told you there is a class called SBXCardManager that probably handles the user's Starbucks cards.

Runtime manipulation using bfinject cycript

Our next step will be combining the information we gathered from the class-dump data and the disassembler and we'll put it to a good use by modifying the app's behaviour at runtime, meaning we are going to run the application and while in memory we'll change some data. cycript is a tool written by Cydia's creator Jay Freeman (aka Saurik), this tool will inject itself into a running process (the iOS app) and give us an interactive console where we can run Objc/javascript code and it will be executed within that process' context.

  • Download cycript from here.
  • Move cycript to /usr/local/bin/ and give it execution permissions.
> mv ~/Downloads/cycript_0.9.594/cycript /usr/local/bin
> chmod +x /usr/local/bin/cycript
  • Launch the Starbucks app and register a new user (this is to get an initial empty Starbucks card)
Starbucks user registration
  • Open a terminal window and run iTunnel to forward the SSH traffic through the USB connection.
> itnl --lport 2222 --iport 22
  • In a different terminal window, SSH into your device.
> ssh -p 2222 root@localhost
> root@localhost's password:
  • Open the app on your device.
  • Use bfinject to inject cycript into the Starbucks running process.
# cd /jb/bfinject
# bash bfinject -P Starbucks -L cycript
bfinject loading cycript
  • On your phone you should see something like the following:
Cycript loaded on the iPhone
  • If this step fails, try force closing the app, relaunch it and run the command again.
  • In another terminal window you can now use cycript to remotely connect to the app using the IP and port shown on the alert by bfinject on your device.
> cycript -r 10.90.14.158:1337
  • Now that you have an interactive console via cycript, you can start injecting some code and modifying the app behaviour. A very simple modification you can make is to change the balance on your Starbucks card (which right now should be zero dollars). Once you remotely connect to the Starbucks app with cycript you should see something similar to:
  • You can see you are interacting with the Starbucks app by executing default iOS API calls:
cy# var app = [UIApplication sharedApplication]
  • You can find a lot of cycript tricks here.
  • To modify your Starbucks card balance, first tap on the PAY button on the Starbucks navigation bar, this will present the Starbucks Card View, in the cycript console type:
cy# var manager = [SBXCardManager sharedManager]
cy# var card = [manager primaryCard]
cy# [card setBalance:23279.74]
cy# [manager replaceCard:card]
  • Now on your device tap on the details button of the Starbucks Card View, this will present the Starbucks Card Details View with the new balance! if you tap on the Back button you'll see the new balance is also updated in the Starbucks Card View.

Note: However, if you tap on the Starbucks Card and bring the Full Screen Starbucks Card View (the one with the barcode), you'll see the balance goes back to zero. This is because this View Controller updates the balance every time it's presented. But with cycript you can override methods, although I'll leave this to you to figure how you can stop this View Controller from updating the card's balance.

This might help:

cy# SBAManageCardViewController.prototype['refreshBalance']  = function() { }

There you go! Now you can fully reverse engineer an iOS app on iOS 11, analyze it and mess with its behaviour at runtime. Have fun!

Feedback on this tutorial is always appreciated!