Repacking iOS applications

A quick and easy guide for binary patching and repacking iOS apps during security audits

TL:DR: You can use Cydia Impactor to resign a patched iOS app. This works way easier then using XCode or the codesign tool.

We recently had to test a iOS application for one of our customers. Like many business applications, the developers integrated common protections, including jailbreak detection and certificate pinning. In cases like this, we commonly use a dynamic Instrumentation framework like Frida to bypass these protections so that we can freely interact with the application and intercept the communication with the server side backend.

Using Frida on a jailbroken device has the advantage that you can avoid a modification of the app binary as the Frida server injects the agent at runtime. It is also possible to use Frida on non-jailbroken devices, however this requires to add the (signed) FridaGadget.dylib to the application. Of course this breaks the signature of the app, therefore it must be resigned and repacked. Take a look here for additional details.

Signing/repacking iOS applicatons can be a bit challenging, especially if you have no background in iOS app development. Thankfully, the fine folks at MWR Labs released a great blog post where they described each step in detail.

We used a jailbroken device for our analysis, hence we didn’t need to modify the app. But while it was quite easy to bypass the jailbreak detection by hooking commonly used functions like “lstat”, we had several issues with the certificate pinning. The main issue was the problem that we were not able to hook the “- isEqualToString” method correctly, and as it looks we were not the first with this problem.

Due to timing constrains, we deviced to no longer deal with this issue and directly patch the binary of the iOS application itself. Of course this requires re-signing the app, however our approach (using Cydia Impactor) makes the entire process less painful.

I will illustrate the entire process (unpacking/patching/repacking/signing) on the jailbreak detection, not the certificate pinning feature. The process is more or less the same.

Unpacking the app

We installed the app via the app store on a jailbroken device. As iOS applications are encrypted for the specific device, we used “Clutch” to dump to binary in a decrypted IPA file. The IPA files was then copied on our local system using scp. Nothing fancy here.

h0ng10-iPhone:~ root# ./Clutch --dump de.mogwailabs.ios.unhackappleapp
ASLR slide: 0x100034000
Dumping <UnhackableApp> (arm64)
Patched cryptid (64bit segment)
Writing new checksum
DONE: /private/var/mobile/Documents/Dumped/de.mogwailabs.ios.unhackappleapp-iOS9.0-(Clutch-2.0.4)-3.ipa
Finished dumping de.mogwailabs.ios.unhackappleapp in 1.2 seconds

IPA files are actually ZIP files, so you can simply run “unzip” to extract them. You can find the actual binary inside the Payload/ directory.

$ unzip unhackableapp.ipa
Archive:  unhackableapp.ipa
  inflating: iTunesArtwork
  inflating: Payload/
  inflating: Payload/
  inflating: Payload/
  inflating: Payload/
  inflating: Payload/
  inflating: Payload/
  inflating: Payload/
  inflating: Payload/

Patching the app binary

When it comes to statically analyzing iOS apps, the Hopper disassembler is (my) tool of choice, mainly because of the good automated annotation for Objective-C based code. However, I will use Binary Ninja here due the great and simple patching features. Binary Ninjas does only provide limited annotation but you can still get some quick results if you are not used to ARM assembly, especially when using the Medium Level IL. Of course you are free to use any disassembler you like.

Most of the time jailbreak detection routines are quite easy to spot, just look for functions like “lstat” or “exit”. These functions are commonly used to detect if the device is jailbroken but not frequently used otherwise. By analyzing the Xrefs of these functions, you can easily identify the actual jailbreak detection routine(s). For simplicity, I already renamed the jailbreak function to a better name (sub_jailbreakdetection) in the screenshots below.

iOS cross references within BinaryNinja The following screenshot shows the entry point of the iOS application. The jailbreak detection function gets directly called at the top.

iOS app entry point with call to jailbreak detection

We can get rid off the jailbreak detection by simply replacing the call to this function with a NOP instruction. Binary Ninja makes this a piece of cake: Just click on the function call and select Patch -> Convert to NOP from the context menu.

patching the iOS app with BinaryNinja

After that, we just need to save the modified binary using “File -> Save Contents As…” and replace the original binary in the extracted IPA archive with this file.

Repacking the application

Normally we would need to resign the modified binary now but this will later be done by Cydia Impactor. We just need to recreate the IPA archive, using the following command:

zip -qr unhackableapp.ipa *

Resigning/installing the new version

As a final step we will use “Cydia Impactor” to re-install the modified application on our device. Cydia Impactor is mostly used to install jailbreak applications on iOS devices. Cydia Impactor is cloded source, but in a nutshell the tool does the following:

  • Logging into your Apple acocunt (using the provided credentials)
  • Creating a developer certificate if no valid certificate is present
  • Unpacking the application and re-signing it with the developer certificate
  • Repacking the application
  • Installing the application on the iOS device

This works perfectly with common jailbreak apps like Yalu but you can use Impactor with any application. Just drop the modified IPA file on the Impactor window and let the Cydia Impactor do the rest. You have to trust the developer certificate on your iOS device by verifying the application on the iPhone (using General -> Settings -> Device Management).

So while we still like and use Frida a lot, this turned out to be an easy way to get rid of common protections like certificate pinning or jailbreak detection when testing an iOS application. If your existing Frida scripts don’t work, it might be quicker to patch the application directly instead of developing the Frida script that hooks the necessary functions.