Chapters

Hide chapters

Advanced Apple Debugging & Reverse Engineering

Third Edition · iOS 12 · Swift 4.2 · Xcode 10

Before You Begin

Section 0: 3 chapters
Show chapters Hide chapters

Section III: Low Level

Section 3: 7 chapters
Show chapters Hide chapters

Section IV: Custom LLDB Commands

Section 4: 8 chapters
Show chapters Hide chapters

15. Dynamic Frameworks
Written by Derek Selander

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

If you’ve developed any type of Apple GUI software, you’ve definitely used dynamic frameworks in your day-to-day development.

A dynamic framework is a bundle of code loaded into an executable at runtime, instead of at compile time. Examples in iOS include UIKit and the Foundation frameworks. Frameworks such as these contain a dynamic library and optionally assets, such as images.

There are numerous advantages in electing to use dynamic frameworks instead of static frameworks. The most obvious advantage is you can make updates to the framework without having to recompile the executable that depends on the framework.

Imagine if, for every major or minor release of iOS, Apple said, “Hey y’all, we need to update UIKit so if you could go ahead and update your app as well, that would be grrrreat.” There would be blood in the streets and the only competition would be Android vs. Windows Phone!

Why dynamic frameworks?

In addition to the positives of using dynamic frameworks, the kernel can map the dynamic framework to multiple processes that depend on the framework. Take CFNetwork, for example: it would be stupid and a waste of disk space if each running iOS app kept a unique copy of CFNetwork resident in memory. Furthermore, there could be different versions of CFNetwork compiled into each app, making it incredibly difficult to track down bugs.

As of iOS 8, Apple decided to lift the dynamic library restriction and allow third-party dynamic libraries to be included in your app. The most obvious advantage was that developers could share frameworks across different iOS extensions, such as the Today Extension and Action Extensions.

Today, all Apple platforms allow third party dynamic frameworks to be included without rejection in the ever-so-lovely Apple Review process.

With dynamic frameworks comes a very interesting aspect of learning, debugging, and reverse engineering. Since you’ve the ability to load the framework at runtime, you can use LLDB to explore and execute code at runtime, which is great for spelunking in both public and private frameworks.

Statically inspecting an executable’s frameworks

Compiled into each executable is a list of dynamic libraries (most often, frameworks), expected to be loaded at runtime. This can be further broken down into a list of required frameworks and a list of optional frameworks. The loading of these dynamic libraries into memory is done using a special framework called the dynamic loader, or dyld.

otool -L 
otool -L /Users/derekselander/Library/Developer/Xcode/DerivedData/DeleteMe-fqycokvgjilklcejwonxhuyxqlej/Build/Products/Debug-iphonesimulator/DeleteMe.app/DeleteMe
/System/Library/Frameworks/CallKit.framework/CallKit (compatibility version 1.0.0, current version 1.0.0)

/System/Library/Frameworks/CoreBluetooth.framework/CoreBluetooth (compatibility version 1.0.0, current version 1.0.0)

/System/Library/Frameworks/Foundation.framework/Foundation (compatibility version 300.0.0, current version 1556.0.0)

/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)

/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.200.5)

/System/Library/Frameworks/UIKit.framework/UIKit (compatibility version 1.0.0, current version 61000.0.0)
/System/Library/Frameworks/
/usr/lib/
otool -l /Users/derekselander/Library/Developer/Xcode/DerivedData/DeleteMe-fqycokvgjilklcejwonxhuyxqlej/Build/Products/Debug-iphonesimulator/DeleteMe.app/DeleteMe
Load command 12
          cmd LC_LOAD_WEAK_DYLIB
      cmdsize 80
         name /System/Library/Frameworks/CallKit.framework/CallKit (offset 24)
   time stamp 2 Wed Dec 31 17:00:02 1969
      current version 1.0.0
compatibility version 1.0.0
Load command 13
          cmd LC_LOAD_DYLIB
      cmdsize 96
         name /System/Library/Frameworks/CoreBluetooth.framework/CoreBluetooth (offset 24)
   time stamp 2 Wed Dec 31 17:00:02 1969
      current version 1.0.0
compatibility version 1.0.0

Modifying the load commands

There’s a nice little command that lets you augment and add the framework load commands named install_name_tool.

(lldb) image list CallKit
[  0] 0484D8BA-5CB8-3DD3-8136-D8A96FB7E15B 0x0000000102d10000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CallKit.framework/CallKit 
pgrep -fl DeleteMe
61175 /Users/derekselander/Library/Developer/CoreSimulator/Devices/D0576CB9-42E1-494B-B626-B4DB75411700/data/Containers/Bundle/Application/474C8786-CC4F-4615-8BB0-8447DC9F82CA/DeleteMe.app/DeleteMe
app=/Users/derekselander/Library/Developer/CoreSimulator/Devices/D0576CB9-42E1-494B-B626-B4DB75411700/data/Containers/Bundle/Application/474C8786-CC4F-4615-8BB0-8447DC9F82CA/DeleteMe.app/DeleteMe
CK=/System/Library/Frameworks/CallKit.framework/CallKit
NC=/System/Library/Frameworks/NotificationCenter.framework/NotificationCenter
install_name_tool -change "$CK" "$NC" "$app"
otool -L "$app"
/System/Library/Frameworks/NotificationCenter.framework/NotificationCenter (compatibility version 1.0.0, current version 1.0.0)

/System/Library/Frameworks/CoreBluetooth.framework/CoreBluetooth (compatibility version 1.0.0, current version 1.0.0)

/System/Library/Frameworks/Foundation.framework/Foundation (compatibility version 300.0.0, current version 1556.0.0)

/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)

/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.200.5)

/System/Library/Frameworks/UIKit.framework/UIKit (compatibility version 1.0.0, current version 61000.0.0)
lldb -n DeleteMe
(lldb) image list CallKit
error: no modules found that match 'CallKit'
(lldb) image list NotificationCenter
[  0] 0FCE1DF5-7BAC-3195-94CB-C6100116FF99 0x000000010b8c7000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/NotificationCenter.framework/NotificationCenter

Loading frameworks at runtime

Before you get into the fun of learning how to load and explore commands at runtime, let me give you a command to help explore directories using LLDB. Start by adding the following to your ~/.lldbinit file:

command regex ls 's/(.+)/po @import Foundation; [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"%1" error:nil]/'
(lldb) command source ~/.lldbinit
(lldb) image list -d UIKit
[  0] /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk//System/Library/Frameworks/UIKit.framework
(lldb) ls /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk//System/Library/Frameworks/
(lldb) process load /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk//System/Library/Frameworks/Speech.framework/Speech
(lldb) process load MessageUI.framework/MessageUI
Loading "MessageUI.framework/MessageUI"...ok
Image 1 loaded.

Exploring frameworks

One of the foundations of reverse engineering is exploring dynamic frameworks. Since a dynamic framework requires the code to compile the binary into a position independent executable, you can still query a significant amount of information in the dynamic framework — even when the compiler strips the framework of debugging symbols. The binary needs to use position-independent code because the compiler doesn’t know exactly where the code will reside in memory once dyld has done its business.

command regex dump_stuff "s/(.+)/image lookup -rn '\+\[\w+(\(\w+\))?\ \w+\]$' %1 /"
(lldb) command source ~/.lldbinit
(lldb) dump_stuff CoreBluetooth
command regex ivars 's/(.+)/expression -lobjc -O -- [%1 _ivarDescription]/'
command regex methods 's/(.+)/expression -lobjc -O -- [%1 _shortMethodDescription]/'
command regex lmethods 's/(.+)/expression -lobjc -O -- [%1 _methodDescription]/'
(lldb) process load PhotosUI.framework/PhotosUI
(lldb) dump_stuff PhotosUI
(lldb) ivars [PUScrubberSettings sharedInstance] 
<PUScrubberSettings: 0x7ffb12818fb0>:
in PUScrubberSettings:
  _usePreviewScrubberMargins (BOOL): NO
  _useTrianglePositionIndicator (BOOL): NO
  _useSmoothingAnimation (BOOL): NO
  _dynamicSeekTolerance (BOOL): YES
  _previewInteractiveLoupeBehavior (unsigned long): 2
  _interactiveLoupeBehavior (unsigned long): 0
  _tapAnimationDuration (double): 0.5
...
(lldb) methods PUScrubberSettings
<PUScrubberSettings: 0x11f80fc48>:
in PUScrubberSettings:
  Class Methods:
    + (id) sharedInstance; (0x11f57092b)
    + (id) settingsControllerModule; (0x11f570be8)
  Properties:
    @property (nonatomic) unsigned long previewInteractiveLoupeBehavior;  (@synthesize previewInteractiveLoupeBehavior = _previewInteractiveLoupeBehavior;)
    @property (nonatomic) BOOL usePreviewScrubberMargins;  (@synthesize usePreviewScrubberMargins = _usePreviewScrubberMargins;)
(lldb) lmethods PUScrubberSettings

Loading frameworks on an actual iOS device

If you have a valid iOS developer account, an application you’ve written, and a device, you can do the same thing you did on the simulator but on the device. The only difference is the location of the System/Library path. If you’re running an app on the simulator, the public frameworks directory will be located at the following location:

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/
/System/Library/Frameworks/
(lldb) ls /
(lldb) ls /System/Library/

Where to go from here?

That /System/Library directory is really something. You can spend a lot of time exploring the different contents in that subdirectory. If you have an iOS device, go explore it!

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now