这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战
引言
作为一名有追求的开发人员,当看到别人家的🐂🍺的 App 时,总是想看看它背后是如何布局的,但又没有源码,越狱就比较麻烦,有没有一种快速查看其布局信息的方法呢?
那么在本文中,我们就以系统的地图 App 来做演示,来看看如何使用 Xcode 的调试器对 iOS 应用程序进行逆向工程。
带着下面两个疑问,让我们开始吧。
如何调试视图层次结构以捕获当前 UI 状态并获取布局信息
如何调试内存图以获得对象层次结构,以及如何实现特定的 API
准备工作
1. csrutil disable; reboot
在开始调试应用程序之前,我们需要禁用计算机的系统完整性保护。这个设置是默认开启的,开启后,我们只能在 Xcode 中调试我们自己的 App。(除非你有其他 App 的源码)
在 Recovery 模式下重启 Mac: 当 Mac 打开后,按 Command (⌘) + r 键,当苹果的 logo 出现在屏幕上时释放按键- 然后,
找到“实用程序”(Utility)并打开终端 - 输入
csrutil disable; reboot这将禁用系统完整性保护和重新 Mac
最后,我们已经准备好开始在 Xcode 推出一款名为 Apple Maps 的逆向工程应用程序。
❗️❗️❗️ 注意: 在完成教程后,不要忘记在 Recovery 下重新启动 Mac,并运行 csrutil enable; reboot 这将再次启用系统完整性保护并重新启动计算机。
2. Attach to Process by PID or Name
- 首先,启动 Xcode,创建一个新项目或者打开你现有的项目
- 在模拟器或者真机上,打开苹果的地图应用
- 接下来,在菜单栏的 Debug 中,选择 Attach to Process by PID or Name
- 搜索 Maps,然后按 Attach
- 一段时间后,进度将从 Attaching to Maps... 变为 Running Maps...。这意味着调试器已成功连接到应用程序。
准备工作做完,接下来开始正式的表演😉
Debug View Hierarchy
点击 Debug View Hierarchy 查看图层的树形结构:
比如,我们想看看底部的 sheet 是如何搭建的,UI 和 操作是如何实现的。
我们先来展开下 UI 层级
这里我们可以看到苹果使用容器和子视图控制器将一个屏幕分割成更小的、可重复使用的部分。
此外,和自己的 App 一样,我们也可以检测它的自动布局警告。
还有非常重要的一点,当我们加入一个项目并需要在代码中找到所需的类时,如果能查看每个视图控制器或视图的名称,就再好不过了。
所以,我们就来看看使用的什么 API 来实现前面提到的底部的 sheet。
Debug Memory Graph
- 首先,点击 Debug Memory Graph 按钮
这让我们可以看到当前活动的对象
从视图层级中,我们发现底部的 sheet 为 SearchViewController。然后在调试导航器中搜索它
点击它之后,我们可以看到右边的依赖图
这让我们知道有一个名为 UIFormSheetPresentationController 的类负责底部 sheet 的展示。通过点击它并显示一个内存检查器,我们可以看到详细的层次结构。
在上图的右下角,我们看到,负责底部 sheet 的对象被称为 UISheetPresentationController。这是自 iOS 15 和 Swift 5.5 以来添加的 API。通过检查文档,我们发现它使用 detents 数组来管理底部 sheet 的 size 配置。
目前只有两种detent: .large() 和 .medium()
然而,除了以上两种之外,应用的底部 sheet 还有一个更小的高度:
苹果是怎么实现的呢?让我们来看看。通过在 debug 导航器中搜索 Detent 关键字,我们可以找到 detents 数组
打印下数组的描述看下:
我们可以看到苹果使用了三个 .custom 的选项
真相只有一个!UISheetPresentationController 的一部分是私有的,开发人员还不能使用。不过,希望 API 很快能够更新,让广大开发者也能进行自定义。
总结
再回到开头提到的两个疑问,原来是用了调式的两大法宝:
如何调试视图层次结构以捕获当前 UI 状态并获取布局信息
- Debug View Hierarchy
如何调试内存图以获得对象层次结构,以及如何实现特定的 API
- Debug Memory Graph