Since its initial release on July 10, 2008, the iOS App Store has amassed over 2.2 million apps. Combine that with a study by Nielsen in 2015 which shows that the average smartphone user interacts with 10 apps a day, and 30 apps a month for a total of over 35 hours spent using apps every month, it’s safe to say that we love our apps.
The way we interact with all of these apps is generally the same: download it from the App Store, find the app’s icon on your home screen, tap it and it magically opens. On the surface, there doesn’t seem much to an app. They all just sit there patiently on your home screen, icons waiting for you to tap on them and bring them to life. But have you ever wondered what actually goes on under the hood? How these apps work, and what they’re made of? Let’s take a look.
Note: throughout this post we will be looking at specific examples from an app which I wrote: RS Xchange.
After finding a new app which you’d like to try out on the App Store, you press the download button and wait a little while for stuff to happen. During this time, iOS is downloading the app in the form of a file with an extension of “.ipa”. So what exactly is this “.ipa” file?
“.ipa” files are actually just “.zip” files. Renaming .ipa to .zip will allow you to extract the contents of the .ipa file in much the same way you extract .zip files. After extraction, you will find a folder with the following structure:
Let’s run through this from top to bottom.
This file is actually a .png file. Adding the “.png” extension to it will allow you to open the file with your image viewing tool of choice. Upon opening the file, you will see that this is just the app’s icon as it is shown on the App Store. This image will have a size of 1024×1024. Here is an example of an iTunesArtwork file:
This file contains lots of information about the app, its developer, when it was downloaded, and which account it was downloaded by. This is what a sample iTunesMetadata.plist file might look like:
The Payload/ folder contains the actual app binary along with all of its resources that it needs to function. Once inside the Payoad/ folder, there will be a single file with the “.app” extension. This file is actually just another folder, and you can open it like any other folder. On MacOS, right click it and press “Show Package Contents”.
The contents of this folder will differ from app to app. This is the stuff that defines each individual app. Here is an example of some of the things you might find:
Let’s run through them top to bottom.
First, you will notice a bunch of images named something like “AppIcon<something>.png”. These images correspond to, as you might have guessed, the app’s icon as you see it on your home screen. There are lots of various sizes of this icon, each one optimized for a different device and different place that it might be shown. For example, iPads have different app icon dimension requirements than iPhones, and the app icon as it is shown in a notification has a different dimension requirement than the app icon which is shown on the home screen.
This file contains some (or all) of the images which the app might use to display once you open it. The images are stored in an optimized format, and you need special tools to take them out and look at them one by one. One such tool which can do this is called the Asset Catalog Tinkerer. Upon exporting, you will get a list of image files which you can open with your image viewing tool of choice. Here is an example of some of the images which you might find inside of the Assets.car file:
[See image gallery at blog.razb.me]This folder has to do with localization. If an app supports multiple languages, each resource which needs to be translated (strings, images, storyboards, xibs) will go into its own folder which ends in “.lproj”. For example, if your app was localized to english and french, you will have three “.lproj” folders: en.lproj, fr.lproj, and Base.lproj. The en.lproj and fr.lproj folders will contain resources which have been converted to their respective languages, and Base.lproj contains the default resources to use in the case when a user’s phone is using a language which is not supported by the app. In most cases, Base.lproj just contains the english version of the app’s resources.
This folder contains all of the dynamic frameworks and libraries which the app relies on. This is just some code which was written outside of the main app, and which is used by the main app. Here is an example of some of the files which might be found in this folder:
Amongst these, you’ll notice that some apps contain files which start with “libswift”, and have the extension “.dylib”. These files make up the Swift runtime, and they are necessary to be included in every app which contains Swift code.
This file contains generic information about the app itself. Things ranging from the app’s name, to the AppIcon files we talked about above, to the permissions which the app might ask for (photos, camera, contacts, etc), and even some internal information such as the unique identifier for the app (called the bundle identifier). Here is a sample Info.plist file:
A lot of interesting information can be found in the Info.plist file. An official summary of everything this file might contain can be found here and here.
Folders which end in .storyboardc contain some information related to the layout of particular screens or views within the app. The presence of these folders indicate that the app was developed using the Interface Builder tool. Within these folders, you will find several .nib files along with another Info.plist file:
The Info.plist file included here is in binary format, and you need to convert it to XML format before being able to read it like normal .plist files. Run this command to convert it to XML:
plutil -convert xml1 Info.plist
Here is what an example Info.plist file could look like inside of a .storyboardc file:
The keys/values within these Info.plist files describe some information relating to the .nib files found alongside them. In this case, the UIStoryboardDesignatedEntryPointIdentifier key is indicating that the view controller with the identifier UIViewController-x1L-Z2-jkR is to be used as the “entry point” for the application, or the first screen to be displayed when the app is opened. The UIViewControllerIdentifiersToNibNames key is mapping identifiers in the compiled .nib files to other .nib files.
.nib files are the result of compiling .xib files using the ibtool utility. .xib files are XML files which define the user interface for a specific element within the app. Everything from individual buttons to entire screens can be defined in .xib files. Here is a sample .xib file:
And the corresponding user interface it produces:
It’s important to note that while developing, the XML of a .xib file is never modified directly by a person. The resulting XML is generated through the Interface Builder tool.
This is an auto-generated file which contains the 4-byte package type followed by the 4-byte signature of your application. For iOS apps, the package type will be “APPL“, and the signature will be 0x3f3f3f3f.
More information about this file can be found here.
This file name will be different from app to app, and the name corresponds to the value for the CFBundleExecutable in the Info.plist file. This is the app’s main executable. It contains all of the code necessary to make the app run. This is the file that gets executed when you tap on the app’s icon on your home screen.
When you download an .ipa file from the App Store, this executable is encrypted, so you can’t take a closer look at what’s inside of it. Fortunately there is a way to get the decrypted version, but we’ll cover that in a future blog post.
This folder contains keys which are used for decrypting the app executables. The contents will look like this:
The .sinf, .supf, .supp, .supx files are used for decrypting, and the Manifest.plist file is used to list all of the relevant .sinf files which are to be used. Here is a sample Manifest.plist file:
This file is specific to the RS Xchange app which we have been inspecting throughout this post. It is just one of the additional resources which are included with the app. Apps can have all sorts of arbitrary resources which they require to function, and you will find them all in the root of the .app folder.
This folder contains information related to code signing. When a developer decides to distribute an app via the App Store, code signing is performed on the executable and all of the other resources included within the app. The purpose of this process is to ensure that the app can not be modified by any malicious third party after the developer has chosen to distribute a version of their app.
There is a single file within this folder called CodeResources. Taking a closer look at this file, it is actually a .plist, and can be opened the same way as any other .plist file. You can find a sample CodeResources file here. (I would include it directly here, but it’s 3000+ lines long)
Each file which is eligible for code signing is included in this file, along with the corresponding hash data which should result from the process of signing each file.
If any of these files are modified after the developer has signed it, the hashes of the files will differ and the app will be unable to be run on a device.
More information about code signing can be found here and here.
This file is once again just a .plist file in disguise. It contains all of the entitlements which the app has requested, such as push notifications and access to iCloud storage. Here is a sample archived-expanded-entitlements.xcent file:
In this example, the only entitlement the app is requesting is for keychain-access-groups. You can find a complete list of the entitlements which an app may request here.
This folder contains some general metadata about the .ipa file. Under the META-INF/ folder, you will find two more files:
You typically don’t need to worry about this folder, and there isn’t much documentation to go with it, but let’s run through the two files found here anyway.
The com.apple.ZipMetadata.plist file contains some information about how the .ipa file was created, along with its uncompressed size. This is what a sample com.apple.ZipMetadata.plist file might look like:
Note that this .plist file is typically found in binary format, and you will need to do some work to convert it to XML format to get the output above. Converting a binary plist to an XML plist can be done with the following command:
plutil -convert xml1 com.apple.ZipMetadata.plist
The next file, com.apple.FixedZipMetadata.bin, is binary file of some sort. There isn’t much documentation about what this is or how to read it, but doing a hexdump on it yields the following:
00000000 4d 64 46 78 01 10 00 00 00 00 00 00 00 00 00 00 |MdFx............| 00000010 00 00 00 00 00 00 00 |.......| 00000017
We’ve officially gone through everything that is found within a typical App Store app file which you download and install on to your device. All of these components work together to provide you with all of the functionality you know and love, like lining up candy, browsing memes, and of course, colouring pictures of cats.
]]>The app is geared towards social media, allowing you to post content and interact with other people’s content in much the same way as Facebook, Instagram, and all the typical social media platforms we’re all familiar with. However, the one thing that seems to set her app apart is the fact that when a post by a fan gets popular enough, there’s a chance that Taylor Swift herself will see it, and even possibly reply. Cool stuff.
Aside from that, the app is jam-packed with Taylor Swift-ness; from exclusive pictures and videos posted by the Swift goddess herself, to animated Taylor-themed emoji (which they’ve creatively dubbed Taymoji), to having Taylor’s songs with lyrics constantly playing at the top of the app. Even the error messages make use of lyrics from Taylor’s songs.
Now the question is, with all of this Swiftiness packed into one app, surely it must also be written in Swift, right? Wrong.
Taylor Swift’s “The Swift Life” is, ironically, not written in Swift.
Some technical details:
There are two main programming languages which are used to write iOS applications: Objective-C and Swift.
In order for an iOS app to support Swift code, it must include something called the Swift runtime which is just a collection of frameworks conveniently packaged under a folder called “Frameworks” inside of the iOS App Store Package (.IPA) file. For a more detailed look at the inner-workings of an .IPA file, check out this post. Taking a look at Taylor’s app, it does not contain the Swift runtime, and therefore it cannot contain any Swift code.
Quite a missed opportunity if you ask me.
I guess the developers just weren’t ready for it.
]]>According to this post on the Apple Developer forums regarding the issue discussed in this topic,
This is a bug in the beta that should already be fixed by the time you read this.
kSecAttrAccessGroupToken is writable only by CryptoTokenKit smart card drivers. Apps can query the keychain using that attribute in order to find items stored on a particular smart card. This attribute is not for any other use.
It appears as though the kSecAttrAccessGroupToken access group was never meant to be writable by any app outside of CryptoTokenKit. This means that normal apps like yours and mine cannot make use of this access group for the purpose of writing our own custom data to it.
It’s also worth noting that the keychain autodelete behaviour which was observed in the iOS 10.3 beta did not end up making it into the public releases. So, for the time being, our beloved keychain continues to persist data beyond app deletion.
Original post below:
On February 7th, 2017, Apple released iOS 10.3 Beta2 to the public, and with that came some changes to the keychain.
The keychain was introduced to iOS devices with the release of iOS 7. The intention of the keychain is to allow a user to securely store login credentials to any number of services so as to not have to type them in every time. Safari uses the iOS keychain and prompts users to save their credentials whenever they login to any websites. However, the keychain is also available to developers. This means that it is possible to store passwords, certificates, and keys without any user interaction.
One of the (potentially unintended) side-effects of the keychain is that anything stored inside of it would persist even after uninstalling the app. This means that anything an app stores in the keychain would potentially live beyond the app itself, and the next time the app is installed and launched, it could read from the keychain and continue as if nothing ever happened.
This property of the keychain is quite useful and heavily relied upon by developers, however, it seems as though this might change with iOS 10.3. As outlined in this post on the Apple developer forums, keychain items are now automatically deleted when the app is deleted. For the purpose of keychain access groups, the keychain items are deleted when the last app in that access group is deleted.
This can be problematic for many apps which rely on the persistence of the keychain in order for their features to work. This was certainly true for me, so I decided to go looking for a workaround.
That is when I stumbled upon the constant kSecAttrAccessGroupToken, of which there does not seem to be much information about on the Internet. Here is the documentation found with this constant:
/*! @enum kSecAttrAccessGroup Value Constants @constant kSecAttrAccessGroupToken Represents well-known access group which contains items provided by external token (typically smart card). This may be used as a value for kSecAttrAccessGroup attribute. Every application has access to this access group so it is not needed to explicitly list it in keychain-access-groups entitlement, but application must explicitly state this access group in keychain queries in order to be able to access items from external tokens. */ extern const CFStringRef kSecAttrAccessGroupToken __OSX_AVAILABLE_STARTING(__MAC_10_12, __IPHONE_10_0);
So, it would seem as though there is an access group, introduced with iOS 10, which all apps on the phone have read/write access to. If this is the case, then anything stored within that access group should persist forever, since any app currently installed may want to read from it in the future.
After testing my hypothesis, this indeed seems to be the case. Saving keychain items with the kSecAttrAccessGroupToken access group persists beyond app deletion. I wrote some sample code demonstrating this here.
So, in order to preserve existing keychain functionality and have entries persist beyond app deletion, a simple work around could be to use this access group. However, there is a caveat:
Since any app on the system can freely read and write to this access group, it is important to consider any security side effects this might have for your app. If you are storing sensitive information in the keychain, it could be worth encrypting keychain entries yourself before saving it off to the keychain.
]]>