How to analyze the implementation of iOS system library

background

In the process of iOS application crash governance, a large number of problems end up in the stack of the system library. Therefore, in order to solve these problems, it is necessary to analyze the internal logic of the system library by reverse means to find the cause of the crash. This article describes how to use DyLD_SHAREd_cache and Hopper for analysis.

Get the system library binaries

Get the system library from the real machine

The first step in doing the reverse analysis is to get the binaries of the system libraries. If you have an iPhone device and the crash you want to analyze happened on that device or the corresponding version of the system, getting the system library binary is as simple as connecting the device to the Mac and selecting it as a Build Target in Xcode. Xcode copies the required files from the iPhone.

These files will be copied to the in ~ / Library/Developer/Xcode/iOS DeviceSupport/directory, according to the equipment system version classify storage, as shown in figure:

Get dyld_shared_cache

But if you don’t have a system that has a problem, such as an old system crash, then you need to use dyLD_SHAREd_cache.

After iOS 3.1, to speed up app startup, DyLD packaged the binaries of all the system frameworks into a large cache file that was loaded when the system started. This file was called DyLD_shared_cache.

Obtained from ipsw

Dyld_shared_cache can be obtained from an IPSW file. Ipsw. me allows you to download ipSW files for various iOS devices and iOS systems. Once downloaded, change the file’s suffix to.zip and unzip it. After decompression folder, double-click the largest DMG file file, and then enter the/System/Library/Caches/com. Apple. Dyld /, can copy dyld_shared_cache export.

Restore dyld_shared_cache

After the export, some tools are needed to restore the system library from the binary. Dsc_extractor is available from Apple, but it is in the dyLD source code and requires some extra Settings to compile.

First of all, we download the code of DYLD from apple’s open source website. I choose the old DYLD-519.2.2 here. The reason why I don’t choose the latest version is that when compiling the new version, There will be an error that the coDesigningtypes.h and diagnose.h headers cannot be found. Dsc_extractor. CPP main (if 0, if 1);

clang++ -o dsc_extractor ./dsc_extractor.cpp dsc_iterator.cpp
Copy the code

After compiling the executable, we can use it to export the system library from dyLD_SHAREd_cache:

./dsc_extractor dyld_shared_cache_arm64e ./frameworks
Copy the code

If the above process seems too tedious, someone on GitHub also provides a compiled DSC_extractor for direct download.

In addition to the tools listed above, the DyldExtractor is a great tool:

python3 -m pip install dyldextractor
dyldex_all dyld_shared_cache_arm64e
Copy the code

After obtaining the binaries of the system library, tools such as Hopper and IDA can be used for reverse analysis.

Use Hopper for analysis

Hopper is a well-known disassembly software with powerful functions. It is optimized for Objective-C disassembly and can generate readable pseudo-code from binary. Therefore, it is a powerful tool for analyzing the execution process of iOS system libraries. Hopper is a fee-paying app that can be used for free for 30 minutes at a time, which is good enough for general analysis problems. We recommend as far as possible to support the legal version.

Here’s a real online example of how to use Hopper. The system stack looks like this:

Thread 0 name: com.apple.main-thread Thread 0: Crashed: 0 libobjc.A.dylib 0x000000018ac37530 _objc_msgSend (in libobjc.A.dylib) + 16 1 UIKitCore 0x00000001b80b6058 -[UIKeyboardImpl _keyboardBehaviorState] (in UIKitCore) + 436 2 UIKitCore 0x00000001b80b63c4 -[UIKeyboardImpl updatedKeyBehaviors] (in UIKitCore) + 32 3 UIKitCore 0x00000001b80b64f8 -[UIKeyboardImpl _updateKeyboardConfigurations] (in UIKitCore) + 172 4 UIKitCore 0x00000001b809240c -[UIKeyboardImpl dealloc] (in UIKitCore) + 356 5 UIKitCore 0x00000001b80c2ae8 -[UIKeyboardInputManagerMux setResponseDelegate:] (in UIKitCore) + 64 6 UIKitCore 0x00000001b80a881c -[UIKeyboardImpl setInputManagerFromCurrentInputMode] (in UIKitCore) + 96 7 UIKitCore 0x00000001b8097d4c -[UIKeyboardImpl setInputModeFromPreferences] (in UIKitCore) + 360 8 UIKitCore 0x00000001b8091d3c -[UIKeyboardImpl initWithFrame:] (in UIKitCore) + 840 9 UIKitCore 0x00000001b808f950 +[UIKeyboardImpl sharedInstance] (in UIKitCore) + 72  10 UIKitCore 0x00000001b8083c50 -[UIKeyboard activate] (in UIKitCore) + 248 11 UIKitCore 0x00000001b80fa98c -[UIKeyboardAutomatic activate] (in UIKitCore) + 124 12 UIKitCore 0x00000001b80fa514 -[UIKeyboardAutomatic willResume:] (in UIKitCore) + 260 13 CoreFoundation 0x000000018b9b8218 ___CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ (in CoreFoundation) + 16 14 CoreFoundation 0x000000018b9b81e4 ____CFXRegistrationPost_block_invoke (in CoreFoundation) + 60 15 CoreFoundation 0x000000018b9b76d8 __CFXRegistrationPost (in CoreFoundation) + 388 16 CoreFoundation 0x000000018b9b7384 ____CFXNotificationPost_block_invoke (in CoreFoundation) + 92 17 CoreFoundation 0x000000018b930c50 -[_CFXNotificationRegistrar find:object:observer:enumerator:] (in CoreFoundation) + 1492 18 CoreFoundation 0x000000018b9b6e34 __CFXNotificationPost (in CoreFoundation) + 692 19 Foundation 0x000000018c3a01a0 -[NSNotificationCenter postNotificationName:object:userInfo:] (in Foundation) + 64 20 UIKitCore 0x00000001b827cf7c -[UIApplication _sendWillEnterForegroundCallbacks] (in UIKitCore) + 228 21 UIKitCore 0x00000001b7b27be0 -[__UICanvasLifecycleMonitor_Compatability activateEventsOnly:withContext:completion:] (in UIKitCore) + 2032 22 UIKitCore 0x00000001b7b25b60 ___82-[_UIApplicationCanvas _transitionLifecycleStateWithTransitionContext:completion:]_block_invoke (in UIKitCore) + 740 23 UIKitCore 0x00000001b7b25828 -[_UIApplicationCanvas _transitionLifecycleStateWithTransitionContext:completion:] (in UIKitCore) + 424 24 UIKitCore 0x00000001b7b2a368 ___125-[_UICanvasLifecycleSettingsDiffAction performActionsForCanvas:withUpdatedScene:settingsDiff:fromSettings:transitionContext:]_block_invoke (in UIKitCore) + 216  25 UIKitCore 0x00000001b7b2b14c __performActionsWithDelayForTransitionContext (in UIKitCore) + 108 26 UIKitCore 0x00000001b7b2a220 -[_UICanvasLifecycleSettingsDiffAction performActionsForCanvas:withUpdatedScene:settingsDiff:fromSettings:transitionContext:] (in UIKitCore) + 240 27 UIKitCore  0x00000001b7b2ef20 -[_UICanvas scene:didUpdateWithDiff:transitionContext:completion:] (in UIKitCore) + 356 28 UIKitCore  0x00000001b7e5f2ac -[UIApplicationSceneClientAgent scene:handleEvent:withCompletion:] (in UIKitCore) + 460 29 FrontBoardServices 0x000000018e3c55d4 ___80-[FBSSceneImpl updater:didUpdateSettings:withDiff:transitionContext:completion:]_block_invoke_3 (in FrontBoardServices) + 220 30 libdispatch.dylib 0x000000018b4857d0 __dispatch_client_callout (in libdispatch.dylib) + 12 31 libdispatch.dylib 0x000000018b42a5d8 __dispatch_block_invoke_direct$VARIANT$mp (in libdispatch.dylib) + 220 32 FrontBoardServices 0x000000018e3ff03c ___FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ (in FrontBoardServices) + 36 33 FrontBoardServices 0x000000018e3fecd8 -[FBSSerialQueue _performNext] (in FrontBoardServices) + 404 34 FrontBoardServices 0x000000018e3ff290  -[FBSSerialQueue _performNextFromRunLoopSource] (in FrontBoardServices) + 48 35 CoreFoundation 0x000000018b9d8f18 ___CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ (in CoreFoundation) + 20 36 CoreFoundation 0x000000018b9d8e98 ___CFRunLoopDoSource0 (in CoreFoundation) + 84 37 CoreFoundation 0x000000018b9d8780 ___CFRunLoopDoSources0 (in CoreFoundation) + 172 38 CoreFoundation 0x000000018b9d36bc ___CFRunLoopRun (in CoreFoundation) + 1000 39 CoreFoundation 0x000000018b9d2fb0 _CFRunLoopRunSpecific (in CoreFoundation) + 432 40 GraphicsServices 0x000000018dbd5798 _GSEventRunModal (in GraphicsServices) + 100 41 UIKitCore 0x00000001b8265c34 _UIApplicationMain (in UIKitCore) + 208Copy the code

On the stack, crash occurs when the _keyboardBehaviorState message is sent to the UIKeyboardImpl instance. The user device is iPhone 6 and the corresponding OS version is 12.5.5.

Use ipSW to extract uikitcore.dylib for 12.5.5 and then drag it into Hopper. When you drag it in, you’ll be asked what file format you want to open it in, usually with the default Settings. Then go to the search bar in the left pane and type -[UIKeyboardImpl _keyboardBehaviorState] to jump to the assembly code for this method:

As you can see, the assembly implementation of this method is quite complex, and there are many of them in the auto-generated annotationsobjc_msgSend, indicating that there are many other method calls inside the method. To which object is the message sent to crash?

Here, we need to combine the crash logs of users for analysis. The user’s original log can be downloaded from our APM system, as well as the log after symbol resolution, both of which can be used for more detailed analysis.

The original log is as follows:

Thread 0 name:  com.apple.main-thread
Thread 0 Crashed:
0   libobjc.A.dylib                 0x000000018ac37530 0x18ac1a000 + 120112 ((null)) + 0)
1   UIKitCore                       0x00000001b80b605c 0x1b79a9000 + 7393372 ((null)) + 0)
2   UIKitCore                       0x00000001b80b63c8 0x1b79a9000 + 7394248 ((null)) + 0)
Copy the code

From the original log, we can see that the offset of objc_msgSend in -[UIKeyboardImpl _keyboardBehaviorState] is 7393372.

Or from the log after symbol resolution:

Thread 0 name:  com.apple.main-thread
Thread 0: Crashed:
0   libobjc.A.dylib                 0x000000018ac37530 _objc_msgSend (in libobjc.A.dylib) + 16
1   UIKitCore                       0x00000001b80b6058 -[UIKeyboardImpl _keyboardBehaviorState] (in UIKitCore) + 436
2   UIKitCore                       0x00000001b80b63c4 -[UIKeyboardImpl updatedKeyBehaviors] (in UIKitCore) + 32
3   UIKitCore                       0x00000001b80b64f8 -[UIKeyboardImpl _updateKeyboardConfigurations] (in UIKitCore) + 172
4   UIKitCore                       0x00000001b809240c -[UIKeyboardImpl dealloc] (in UIKitCore) + 356


Binary Images:
       0x1b79a9000 -        0x1b8a8dfff  UIKitCore arm64 <005cfa346e6a3f36ba96e3db92f09362> /System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore
Copy the code

The offset address can be calculated as

0x00000001b80b6058 - 0x1b79a9000 = 0x70D058 = 7393368
Copy the code

Navigate -> Go To File Offset in the Hopper box To Navigate To the specific number of rows at the time of the crash, as shown in blue:

Click on the Hopper button in the upper right corner,

To convert this assembly code to pseudocode:

As you can see from the diagram, the message crashes while sending the hasCandidates message to UIKeyboardImpl’s m_candidateResultSet instance variable. Therefore, we need to continue to analyze the assignment and usage scenarios of this instance variable to see if there is the possibility of wild Pointers, and the analysis process is similar.

This is the idea of using Hopper to analyze the internal implementation of the system library.