Reverse Engineering iOS Apps - iOS 11 Edition (Part 2)
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 becauseclass-dump
couldn't parse it.
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 thePseudo-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)
- 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
- On your phone you should see something like the following:
- 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 withcycript
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 thecycript
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 theBack
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!