In this short article I’ll try to explain what are the main steps to analyze an iOS app. Since I’ve writen similar posts related to Android I thought I could devote some of spare time writing about the steps required to analyze iOS apps/binaries. But first of all let’s start with:
What’s an iOS app?
In a nutshell here are the main characteristics:
- Objective-C / C / C++ compiled (ARM) executable
- mostly encrypted using Apple’s Fairplay DRM
- it runs in a sandbox
- it’s installed by the user
mobile
- apps come as an IPA file which is the counterpart to Android’s APK
Now that you roughly know what an iOS app is let’s have a look at the most common blackbox pentesting tools out there. In this post I’ll focus only on static analysis. Dynamic analysis (also known as runtime analysis) will be covered in a future post.
Binary Analysis Tools
Assuming you’ve already jailbreaked your device, you’ll definitely need these tools:
- SSH - For connecting to the device
- ipainstaller - Install IPA files
- otool - object file displaying tool
- class-dump-z - Examine Mach-O files and dump segments as Objective-C declarations
- Dump encrypted app
- dumpdecrypted - Dumps decrypted mach-o files from encrypted iPhone applications from memory to disk
- Clutch
IPA
ipa files are archive files which are usually encrypted using Apple’s FairPlay DRM. In a nutshell an ipa file consists of:
{% img http://oi60.tinypic.com/15q2ici.jpg 600 200 “IPA-File” “IPA-File” %}
The App binary is the target to be analyzed. It’s compiled for ARM and used the Mach-O(mach object) file format. Check out next section for more detailed information.
Installing the App
Usually you can manually install apps by using ipainstaller:
|
|
This will install your app under /var/mobile/Applications
. Just do a grep
to find out to which folder your app was copied to.
Mach-O
The Mach-O binary consists of 3 components:
{% img https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/MachORuntime/art/mach_o_segments.gif 600 200 Mach-O Mach-O %}
Header
The header contains basic file type information like architecture and several flags. Using otool you can have a look a the headers:
|
|
Sometimes you’ll get an application that is built for multiple architectures. These applications then consist of multiple Mach-O files and are called fat or universal binaries. Mach-O fat binaries not only group completely different CPU architectures (PowerPC, Intel) but also 32- or 64-bit versions of an architecture. Besides that you’ll also get different CPU subtypes bundled in one binary. The device running the binary will choose the “part” of the binary it can best support. Also have a look at this great article.
Here is an example of a fat binary:
|
|
Load commands
The load commands are located directly after the header and specify the logical structure of the binary (as a file) and it’s representation in the virtual memory (using offsets). Besides that you’ll be able to get:
- the symbol table
- shared library details
For my understanding it’s pretty much the same as ELFs segments and sections:
{% img http://oi59.tinypic.com/3478ltk.jpg 600 200 ELF ELF %}
Load commands also define whether an application is encrypted or not. To have a look at the those run:
|
|
Raw segment data
In the Mach-O file you’ll also have raw data for the segments specified in the load commands. One segment can consist of multiple sections.
Runtime protection features
iOS has several mechanisms which prevent application from being compromised at runtime. In order to understand the security issues that affect iOS applications, it is important to understand and to known the security features of the platform. The main security features of iOS are:
-
Code signing
- Ensures that all applications come from a approved source (using Apple-issued certificates)
-
Generic exploit mitigations
- Address Space Layout Randomization (ASLR)
- Usually compiled using
-fPIE –pie
- Usually compiled using
- Non Executable Memory (ARM’s Execute Never feature)
- Stack Smashing Protections (SSP)
- Usually compiled with
–fstack-protector-all
flag
- Usually compiled with
- Address Space Layout Randomization (ASLR)
-
Sandboxing
- run applications as non-privileged user
- 3rd-party apps are restricted in accessing files stored by other apps
-
Memory Management
- Automatic Reference Counting (ARC) protects applications from memory coruption issues by letting the compiler do the memory management stuff
Keeping all this stuff in mind, let’s pickup some binary and go for it.
Analyzing the binary
Comparing Android to iOS I must admit you’ll have to overcome more (technical) challenges for a successful analysis. iOS uses binary files (instead of bytecode). Having that said I’ll be using otool (which seems to be the equivalent to readelf) to inspect the binary.
Architecture
Let’s first determine the architecture the binary was compiled for:
|
|
Or using old-school file
:
|
|
Also have a look at this cool list.
Encryption
Usually the ipa
file will be decrypted at runtime by the kernel’s mach loader. If the binary is encrypted or not is easily found out using:
|
|
In this case the binary file is not encrypted. Let me show an example where the binary is encrypted:
|
|
Runtime protections mechanisms
This time I’ll show you how to extract some valuable information from the binary itself regarding some runtime protection mechanisms:
- ASLR
- Usually the binary is compiled using the
PIE
flag
- Usually the binary is compiled using the
|
|
Have you noticed the PIE
flag at the end of the list?
- Stack Smashing Protection
- iOS applications usually use stack canaries
- therefore you should find certain symbols inside the binary (like
_stack_chk_guard
and_stack_chk_fail
)
|
|
- Automatic Reference Couting
- this option can be enabled by activating the compiler option “Objective-C Automatic Reference Counting”
- binaries built with this option should include symbols called
_objc_release
,_obj_autorelease
,_obj_storeStrong
,_obj_retain
|
|
Dangerous functions
Beside the previosly mentioned symbols we can also seek for symbols aimed at (classical) memory management mechanisms like malloc
and free
. Their presence indicate that the application has its own memory management which is the opposite to ARC. While this is not always a bad thing it could easily lead to some memory related vulnerabilities if not handled properly.
|
|
Sometimes you’ll find goodies like
strcpy
:)
Understand the App
Now that we have examined the binary we should proceed and try to “understand” the application. This means we have to look at it from a logical perspective and identify its main components. Afterwards one can go into detail and analyze only certain parts of the application which might be of interest.
Using class-dump-z
we’ll dump the class information:
|
|
Every class name seems to be encrypted. That’s a good hint you should decrypt the binary in case you haven’t done so yet.
Decrypt binary
Since every application downloaded from the AppStore is encrypted using Apple’s FairPlay DRM you’ll have to decrypt them before starting your analysis. For this step I’ll be using clutch
to dump the relevant data from memory to disk.
|
|
Now that you have decrypted to binary and got a fresh new IPA file, you’re ready to unpack it:
|
|
Afterwards you can have a look at the new binary using class-dump-z
:
|
|
Much better, isn’t it? :)
Disassemble binary
I don’t want to be too specific and go into too much detail. A good disassembler could save you a lot of time. I really like Hopper because it’s free and easy to use. For the geeks out there feel free to throw your binary into IDA and let the bin rock. Particularly noteworthy is also radare2 which is unix-like reverse engineering framework.
Conclusion
Binary analysis can be a hell of a lot of fun if you have the right tools. Especially when you’re not used to Apple’s universe and don’t have a Mac OS machine it could be useful to jailbreak your smartphone/tablet and install your tools there. Gather as much information as you can get to get a pretty precise image of what you’re dealing with. Disassemble your binary to get more in “contact”. Afterwards run it and do some runtime analysis.