AIR ARMOR is a security solution of AIR; AIR is a component of the LINE GAME PLATFORM.
You can refer to Seunghoon's posting on AIR GO and APK Signing for information on the APK signature scheme. For this posting, I'll explain about iOS code signing, one of Apple's security mechanism. Code signing is to verify the integrity of the software code and confirm the signer (who wrote the code). I'll also cover checking the integrity of iOS Mach-O binaries later using the code signature struct. Let me first give you some ideas about iOS code signing.
What is iOS code signing?
iOS devices only run apps that are code signed with the certificate issued by Apple, whether it is an app downloaded from App Store or app built for the purpose of testing. This code signing must be done with a certificate issued by Apple.
Apps downloaded from App Store are code signed with Apple's certificate as follows:
Those apps under development for distribution or testing by developers are signed with the development certificate issued by Apple.
The following is a sample of development certificate for ad hoc distribution.
To install an app signed with an Apple's Development Certificate, you must have a provisioning profile on your device. You need to have a provisioning profile installed on the device specified in the provisioning profile. Only then can you install and run an iOS app signed with the developer certificate.
What is provisioning profile?
Apple defines a provisioning profile as "a type of system profile used to launch one or more apps on devices and use certain services". You can create a provisioning profile in Xcode by enabling automatic signing (personally recommended) or go for manual signing at Apple Developer Program.
The following window appears for ad hoc distribution of a sample app.
When you select the Automatically manage signing option in the window above, Xcode will automatically generate a provisioning profile during the build. You can check automatically generated profiles in the following path: ~/Library/MobileDevice/Provisioning Profiles
.
$ ls ~/Library/MobileDevice/Provisioning\ Profiles
1axxxx49-xxxx-xxxx-xxxx-c2xxxxxxxxff.mobileprovision
a8xxxxec-xxxx-xxxx-xxxx-5cxxxxxxxx22.mobileprovision
If you select the Manually manage signing option, you need to choose a provisioning profile you created manually with your developer account, as shown below. However, you cannot make changes to automatically generated provisioning profiles and save them as custom provisioning profiles.
What does a provisioning profile contain?
You can find the provisioning profile in the app package file (.ipa
file). When you unzip the .ipa
file, you will see a file named embedded.mobileprovision
in the Payload
folder (e.g. Payload/sample.app/embedded.mobileprovision
). This is your provisioning profile. You can use the following command to inspect the provisioning profile or just open the profile in a text editor.
$ security cms -D -i embedded.mobileprovision
Provisioning profiles have information on development certificates, provisioned devices, entitlements, and expiration dates. If where you launch the app doesn't match the information in the provisioning profile, your app won't be launched.
Development certificates
When you take a look at the DeveloperCertificates
key in a provisioning profile, you will find a string encoded in base64.
As openssl
command cannot interpret data encoded in base64, you need to convert this into PEM (Privacy-Enhanced Mail) format so that the command can understand the data. You can do this by copying the string, adding -----BEGIN CERTIFICATE-----
at the top and -----END CERTIFICATE-----
at the bottom, and saving the file. (For this posting, I saved the file as "sample.txt
.")
Using the openssl
command, you can check the information of the certificate saved in sample.txt
as follows:
$ openssl x509 -in sample.txt -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number: XXXXXX... (XXXXXX... )
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = US, O = Apple Inc., OU = Apple Worldwide Developer Relations, CN = Apple Worldwide Developer Relations Certification Authority
Validity
Not Before: Oct 26 09:45:00 2018 GMT
Not After : Oct 26 09:45:00 2019 GMT
Subject: UID = XXXXXXXX, CN = iPhone Developer: XXXXXX (XXXXXXXX), OU = XXXXXXXX, O = XXXXXX, C = US
Subject Public Key Info:
...
Entitlements
After checking the certificate information, you can find entitlements information, as shown below, right underneath the certificate information.
Entitlements specify which services the app is allowed to use and under what situation. For your information, as this provisioning profile sample is a debug build, the get-task-allow
option is set as true
to allow process debugging.
What is listed in the <array>
above depends on what capabilities you selected in the Xcode Capabilities tab. Let me enable Access WiFi Information and Apple Pay capabilities as follows:
When you create a provisioning profile with the configurations above, you can see these capabilities are added to the Entitlements
as keys with respective values.
Expiration dates
You can check the expiration date of a provisioning profile with the ExpirationDate
key. Your app will not launch after this expiration date.
Provisioned devices
You can view a list of registered devices for this provisioning profile under the ProvisionedDevices
key. Your app will only run on these devices.
A provisioning profile that allows an app to run on any device, for such as ad hoc distribution, will have the ProvisionsAllDevices
key as true
.
Verifying the integrity of binaries
Code signed Mach-O binaries have a code signature struct, which is used to verify the integrity of the Mach-O binaries.
What is a Mach-O binary file?
Before we take a deep dive, let me briefly explain about Mach-O binary files. Apps that run on iOS (.ipa
files) have a Mach-O binary file. Mach-O is a standard executable format in iOS and macOS. iOS executes a Mach-O binary file to launch an app.
$ file sample
sample: Mach-O 64-bit executable x86_64
$ ./sample
sample!
How to find a Mach-O binary file
This is how you find a Mach-O binary file. First, you need to unzip the ipa
file, and you will find the Info.plist
file in the Payload
folder (e.g. Payload/sample.app/Info.plist
). When you open Info.plist
, you will find the Executable file element, which is the Mach-O file executed to launch an app. The following is how the Info.plist
file looks like.
The following diagram shows a high-level description of the structure of our sample Mach-O binary file.
Now, let's dive a little bit further into Mach-O binaries by looking at its components.
machHeader
You can check whether the file is a Mach-O file or not by looking at the magic
value of the machHeader
struct.
The following table shows a brief description of each member of the Mach-O Header struct.
Member | Description | Example | Reference |
---|---|---|---|
magic | Indicates whether the file is a Mach-O file and determines the byte order. | 0xfeedface : Mach-O 32 bit format and byte order matched to the endianness of the host PC |
<mach-o/loader.h> |
cpu_type | Indicates a type of CPU | 0xc : arm |
<mach/machine.h> |
cpu_subtype | Indicates a subtype of CPU | 0x9 : arm v7 |
<mach/machine.h> |
file_type | Indicates a type of file for this header | 0x2 : Executable file |
<mach-o/loader.h> |
num_load_commands | Indicates the number of load commands. | 34 : Total of 34 load commands |
- |
size_of_load_commands | Indicates the total size of load commands in bytes. | 3792 : Total size of 34 load commands, which is 3792 bytes |
- |
flags | Indicates what optional features of the Mach-O file format this binary file is using. | - | <mach-o/loader.h> |
Load commands
The machHeader
struct is directly followed by a list of load commands (LoadCommand
). If the result of parsing the machHeader
struct indicates that the file is a Mach-O file, then the load command list is parsed. Load commands have a lot of information including the location of segments, the name of dynamic libraries and the location of symbol table. Among other things, I'll focus on the code signing related load command, which is LC_CODE_SIGNATURE
(LoadCommand
of CODE_SIGNATURE
).
LC_CODE_SIGNATURE
You can see the LoadCommand
related to code signing information of the Mach-O file.
You can look up the information on LC_CODE_SIGNATURE
, using the otool
command.
$ otool -l Payload/iOSSample-mobile.app/iOSSample-mobile | grep LC_CODE_SIGNATURE -A3
cmd LC_CODE_SIGNATURE
cmdsize 16
dataoff 3188480
datasize 59808
dataoff
indicates the offset of CodeSignature
. When you follow this information one by one, you can find all of code signing related information. I'll not go into further detail about how to find this information.
The following shows a high-level structure of CodeSignature
.
Let's take a closer look at the following items that compose the CodeSignature struct:
CodeDirectory
In the CodeSignature
struct, you can find the CodeDirectory
, which has the hash values of a specific file or executable binary file pieces. When you look at the CodeDirectory
struct of our sample app binary file, you can find HashSlot codeHash and HashSlot specialHash at the bottom of the list.
codeHash
codeHash
has the hash values of the binary file for each pageSize
(0x1000
). The following diagram represents the codeHash
of our sample app.
When the code is modified, the hash value of the page corresponding to the code modifications will change. Since this hash value will not match to the hash value in the codeHash
, iOS will know that the code has been modified.
specialHash
Now, let's take a look at specialHash
. We got the values of each element in the specialHash
array by parsing the Mach-O file. The elements and each element value is as follows.
Array index | Contains | Description | Hash (of our sample app) |
---|---|---|---|
0 | Entitlement | Bound in code signature | 4b255acb014ab5dc8cd63f5120baeef19309e340 |
1 | Application Specific | Application Specific is not currently used as indicated by the code related to the Apple Mach-O code signing. | 0000000000000000000000000000000000000000 |
2 | Resource Directory | Resource directory indicates _CodeSignature/CodeResources file in the ipa file. |
35b65bb61f6b617e9a944cdada31cb78b47ab393 |
3 | Internal requirements | Internal requirements indicate Requirements in the file. | cdbf07382a5a26998e34dc9d80070fc5db8c9230 |
4 | Bound Info.plist (Manifest) | Bound Info.plist indicates the Info.plist file in the .app directory. |
5108d83c00eb7f294fb73914abdb0a1c977b92f2 |
CodeResources
The resource directory points to the _CodeSignature/CodeResources
file (e.g. Payload/sample.app/_CodeSignature/CodeResources
) in the ipa
file. The CodeResources
file lists up the checksum of the resource files in the app file. You can see the content of the CodeResources
file of our sample app below.
We can check the hash value of sha-1 by using the shasum
command. The hash value retrieve by the command is identical to the sample value identified in specialHash[2]
.
$ shasum Payload/iOSSample-mobile.app/_CodeSignature/CodeResources
35b65bb61f6b617e9a944cdada31cb78b47ab393 Payload/iOSSample-mobile.app/_CodeSignature/CodeResources
Bound Info.plist
The "Bound Info.plist" indicates the Info.plist
file in the .app
directory. The Info.plist
file has basic information on the app such as the name, version and filepath of the app icon. The value returned is identical to the sample value identified in specialHash[4]
.
$ shasum Payload/iOSSample-mobile.app/Info.plist
5108d83c00eb7f294fb73914abdb0a1c977b92f2 Payload/iOSSample-mobile.app/Info.plist
Requirements and Requirement
A Requirement
is a set of rules used to verify code signing. The number of Requirement is defined by the count member of the Requirements struct. You can check out the information on Requirements, using the codesign command as shown below.
$ codesign --display -r- Payload/iOSSample-mobile.app/iOSSample-mobile
Executable=Payload/iOSSample-mobile.app/iOSSample-mobile
designated => identifier "armor.sdk.sample.cocos2dx.ios" and anchor apple generic and certificate leaf[subject.CN] = "iPhone Distribution: XXXXXXX (XXXXXXX)" and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */
To verify the checksum, we will copy all of Requirements andRequirement
fields and save it as a file, called requirements.txt
.
Let's check the checksum of the requirements.txt file, using the shasum
command as below. The value returned is identical to the sample value identified in specialHash[3]
.
$ shasum requirements.txt
cdbf07382a5a26998e34dc9d80070fc5db8c9230 requirements.txt
Entitlement
The CodeDirectory
has the hash value for Entitlement
in the binary file. You can find Entitlement
in the CodeSignature
struct, as shown below.
Let's copy the Entitlement
struct, including the field magic
, length
and data
, into a file. Save the file with the name, entitlements.txt
.
And run the shasum
command with file containing the Entitlement
struct. The value returned is identical to the sample value identified in specialHash[0]
.
$ shasum entitlement.txt
4b255acb014ab5dc8cd63f5120baeef19309e340 entitlement.txt
BlobWrapper
BlobWrapper
has the CMS (Cryptographic Message Syntax) signature. You can view the information on BlobWrapper
with jtool.
$ jtool --sig -v Payload/iOSSample-mobile.app/iOSSample-mobile
...
Blob 4: Type: 10000 @41788: Blob Wrapper (4802 bytes) (0x10000 is CMS (RFC3852) signature)
BlobWrapper
has data on the code signing of the CodeDirectory
as mentioned earlier and certificates used for code signing of such data. If you want to view the content of Certificates, extract the data of BlobWrapper
from the binary file and save it as a file. In our exercise, let's name it blobwrapper.txt
.
You cannot open this text file with a text editor, but you can check overall content using the openssl
command, as shown below.
$ openssl pkcs7 -inform der -in blobwrapper.txt -print -noout
PKCS7:
type: pkcs7-signedData (1.2.840.113549.1.7.2)
d.sign:
version: 1
md_algs:
algorithm: sha256 (2.16.840.1.101.3.4.2.1)
parameter: NULL
contents:
type: pkcs7-data (1.2.840.113549.1.7.1)
d.data: <ABSENT>
cert:
cert_info:
version: 2
serialNumber: 134752589830791184
signature:
algorithm: sha1WithRSAEncryption (1.2.840.113549.1.1.5)
parameter: NULL
issuer: C=US, O=Apple Inc., OU=Apple Certification Authority, CN=Apple Root CA
validity:
notBefore: Feb 7 21:48:47 2013 GMT
notAfter: Feb 7 21:48:47 2023 GMT
subject: C=US, O=Apple Inc., OU=Apple Worldwide Developer Relations, CN=Apple Worldwide Developer Relations Certification Authority
key:
algor:
algorithm: rsaEncryption (1.2.840.113549.1.1.1)
parameter: NULL
public_key: (0 unused bits)
0000 - 30 82 01 0a 02 82 01 01-00 ca 38 54 a6 cb 0.........8T..
...
When you change the content of the CodeDirectory
, the CMS signature will change even if you use the same certificate for code signing. So even if an attacker changes the code and updates the content of the CodeDirectory
in line with the changed code, you can ensure the integrity of your app by verifying the CMS signature with your key.
Conclusion
iOS does not stop at verifying the hash value of the files related to the binary file, but goes further to ensure the integrity of the app by verifying code signing. This process allows iOS to prevent a manipulated app from running on a device of random users.
AIR ARMOR provides additional security features on top of the basic security feature using the code signing principles of a Mach-O binary file. Please check out its security features here.